1.0.0 Data categorization, dimension reduction, and dimension
transformation
Last week we looked at an analysis of RNAseq data through a number of
methods starting with broad-level volcano plots and moving towards
gene-level expression visualizations with dotplots. In between we
stopped to take a look at heatmaps. In this instance we simply used
heatmaps to convey expression levels of multiple genes across one or
more samples.
Looking more broadly, we now wish to ask questions such as “how
similar are our samples?”, “can our samples be grouped or
categorized in some way?” and “is there an underlying structure or
architecture to our data?” We briefly discussed scatterplot
matrices to help analyse our sample quality amongst
replicate experiments.
We can study these questions using a number of techniques that range
from clustering/categorization to projection of high-dimensional data
onto a lower set of dimensions (usually 2 or 3).
1.1.0 What kind of data do we care about?
We cannot begin our journey until we talk about the nature of the
data we are interested in examining. Usually, our data will consist of
many samples (observations) with some number of features captured about
each observation. For example, with RNAseq data we could consider the
measurements we capture for each gene as a separate
feature/variable/column.
Conversely you may have hundreds or thousands of samples you’d like
to categorize in some way (or show that you can categorize) with just a
smaller set of features. For every nut, there is a tool of
sorts for cracking it!
We’ll start with a modified version of the PHU age group dataset that
we’ve seen before from Lecture 02 and 03. The demographics data has been
modified to include a set of normalized values (individuals per 100k)
that was calculated based on StatsCan 2017 census data. Modeling off of
these data, the 2022 sizes for each age group were
estimated. Case, death, and hospitalization counts were normalized based
on these subgroup sizes to generate values per 100K individuals. We’ll
be working with this 2022 dataset mainly because it utilizes more
fine-grained binning of our age-groups than more recent dataset from the
Ontario government archives.
The updated dataset will be found in
phu_demographics_census_norm.csv and we’ll use it to guide
us through two sections of today’s lecture.
Let’s begin by loading the data and taking a look at its
structure.
# Import the normalized demographics data
covid_demographics_norm.df = read_csv(...)
# Look at it's structure
str(covid_demographics_norm.df, give.attr = FALSE)
1.2.0 Looking for trends/groups in your data
Let’s begin looking at our covid_demographics_norm.df.
Using a series of data sets, we’ve created a consolidated dataset
with:
Cumulative cases, deaths, and hospitalizations due to COVID-19
within each age group per PHU.
Representation of each age group within each data category as a
percent of total incidents in each PHU.
Using 2017 census data, the number of cases per 100,000
individuals normalized by estimated population size for each age group
within each PHU.
The question we want to answer is: of the 34 public health units,
which look most similar based on the normalized case
data for each age group? In order to visualize this data,
we’ll want to convert our current long-form data that looks something
like this:
| Algoma |
0 to 4 |
181 |
… |
3302 |
0 |
164 |
| Algoma |
12 to 19 |
412 |
… |
4464 |
0 |
54 |
| … |
… |
… |
… |
… |
… |
… |
Into something like this:
| Algoma |
117840 |
cases |
3302 |
4464 |
7570 |
4301 |
4890 |
2331 |
4116 |
| … |
… |
… |
… |
… |
… |
… |
… |
… |
… |
We need to do the following to the dataset
- Select just the variables we are interested in.
- pivot out the age group data into its own columns so we have 1
observation (row) per PHU.
- Correct the position of the age groups (5 to 11 needs to be
moved).
# Create a wide-format version of our normalized data
covid_demographics_norm_wide.df <-
# Start by passing along the long-form normalized data
covid_demographics_norm.df %>%
# Select for just the PHU, age_group, population size and cases/hosp/deaths_per_100k
dplyr::select(c(3,4,11,13:15)) %>%
# Pivot the data to a longer format
pivot_longer(cols = -c(1:3), names_to = ..., values_to = ...) %>%
# Get rid of the suffix of "_per_100k"
mutate(category = ...(string = category, pattern = "_per_100k")) %>%
# Pivot the age_group/per_100k data out wider
pivot_wider(names_from = ...,
values_from = ...
) %>%
# Move the "5 to 11" category to after "0 to 4". You could use a longer "select" call to do this too!
relocate(., `5 to 11`, .after=`0 to 4`)
# Take a look at our resulting dataframe
head(covid_demographics_norm_wide.df)
1.2.1 Cast our data to a matrix for heatmaps
At this point, we would normally be prepared to visualize our data
with ggplot but our data is in wide-format
and we’ll be using a package that prefers to work with
matrix data. In that case, we need to strip the data
down further because matrix data must be all of the same type and we
want to work with numeric data! We’ll use as.matrix() for
our conversion.
- We’ll filter our dataset to only include the
“cases” data we saw above, and then drop the
category variable from it.
- We’ll move the
public_health_unit information over to
the row names of our matrix so we can still track our data
properly.
- We’ll still keep our
population_2022 column of data but
we’ll have to remember that it’s there.
# Now we need to make our matrix and assign row names.
# It's kind of awkward to need this requirement.
# Cast our matrix and drop the first column
covid_demographics_norm.mx <- covid_demographics_norm_wide.df %>%
# Filter for just case data
dplyr::filter(...) %>%
# Drop the PHU and category data
dplyr::select(-c(public_health_unit, category)) %>%
# Convert to a matrix
as.matrix()
# Set the row names using the information from the data frame
rownames(covid_demographics_norm.mx) <-
covid_demographics_norm_wide.df %>%
# Pull the PHU names
... %>%
# Create a unique vector of them
...
# Take a peek at our data
head(covid_demographics_norm.mx)
1.3.0 Plot and reorder our PHUs as a heatmap with
Heatmap()
From the ComplexHeatmap package we can use the function
Heatmap() to generate a heatmap that can do a little more
than what we were doing with our ggplot2 version. A nice
aspect of using Heatmap() is that we can ask it to reorder
our samples along both the rows and columns. At the same time we can
present the relationship between columns elements or row elements in the
form of dendrograms.
1.3.1 How do we reorder our data via clustering?
In its current form, we have our ordered our data along the columns
in an ascending ordinal arrangement. While this makes sense to us now,
it may not help to visually identify the strongest trends or groupings
in our data. Clustering attempts to bring order to our data by grouping
data according to specific algorithms.
Overall single points are usually grouped together as neighbours with
the “nearest” neighbours determined by a metric of some kind. These
clusters are then further grouped using the same metrics until the
entire set is presented in these ordered groups. These
groupings/relationships can also be presented as dendrograms. In our
current case, we aren’t concerned with determining groups per
se but rather with just connecting them by their similarity and
then creating a hierarchical order.
Our data is usually coded in n-dimensional space, depending on the
nature of our dataset. For covid_demographics_norm.mx our 7
columns are coded by data from 34 PHUs meaning each column is coded in
34 dimensions or 34 features. Conversely, our 34 rows represent PHUs
each coded by data across 7 age groups and therefore 7 dimensions.
Important or helpful parameters to run Heatmap():
matrix: our matrix object. It must be a
matrix, and not a data frame. It could also be a vector
but that becomes a single column.
col: determines the colours used for the image.
name: the name/title of the heatmap (also use as the
legend title by default)
row_* : set our row text properties including titles
and labels:
row_title, row_title_side,
row_title_gp (graphic properties)
row_names_side row_names_gp
- column properties can also be changed using
column_*
cluster_rows and cluster_columns: logical
parameters to determine if clustering should occur (default is
TRUE)
show_heatmap_legend: whether or not to show the heatmap
legend
heatmap_legend_param: Set the title of the legend
specifically is list(title = "x")
show_[row/col]_dend: Whether or not to show dendograms
for the row/column
There are actually a lot of options but these should help us
make a basic one. Let’s plot our data with and without a dendrogram to
compare.
?Heatmap
# Create a Heatmap object
cases_hmap <-
# Supply our matrix minus the populations size
Heatmap(...,
cluster_rows = ..., cluster_columns = ..., # Don't cluster on either rows or columns
# Use column_title as the title of our heatmap
column_title = "Heatmap of COVID-19 cases in PHUs by age group: unclustered",
# Rotate the legend horizontally and give it a title
heatmap_legend_param = list(title = "cases per 100K individuals",
legend_direction = "horizontal"),
# Rotate column names to horizontal
column_names_rot = 0,
column_names_center = TRUE
)
# Plot the heatmap
...(cases_hmap,
# Plot the legend on the bottom
heatmap_legend_side = "bottom"
)
# Create a Heatmap object
cases_hmap <-
# Supply our matrix minus the populations size
Heatmap(covid_demographics_norm.mx[,-1],
cluster_rows = ..., cluster_columns = ..., # Cluster on both rows and columns
# Use column_title as the title of our heatmap
column_title = "Heatmap of COVID-19 cases in PHUs by age group: clustered",
# Rotate the legend horizontally and give it a title
heatmap_legend_param = list(title = "cases per 100K individuals",
legend_direction = "horizontal"),
# Rotate column names to horizontal
column_names_rot = 0,
column_names_center = TRUE
)
# Plot the heatmap
draw(cases_hmap,
# Plot the legend on the bottom
heatmap_legend_side = "bottom"
)
1.3.2 Concatenate multiple heatmaps together into a
HeatmapList object
Our heatmap above is drawn from a single dataset category -
cases - but it helps us to see that there are some
strong signals that differentiate between our different PHUs. Now this
is a 34x7 grid where we can investigate all of the data in our dataset.
What happens when we want to produce this kind of data from our other
two metrics of hospitalizations and deaths?
To accomplish this, we can repeat our steps and create 3 separate
Heatmap objects but we can plot
them together as a single complex heatmap. In fact, rather than
store multiple heatmap objects, we’ll create a HeatmapList
object by concatenating together multiple Heatmap objects
using a for loop.
We’ll recreate our code from above but simplify the heatmap code a
little. While we’re at it, we’ll also convert our colourscheme to
viridis while we’re at it
# Create a list of heatmap objects from our demographic data
hm_list <- NULL
# Create some quick vectors to help ourselves out
categories <- ...
# Store the rownames we want to use on our matrices
mx_rownames <- covid_demographics_norm_wide.df %>% pull(public_health_unit) %>% unique()
# Use a loop to separate the data by category
for (i in categories) {
# Create a temporary matrix of the specific category data
temp_mx <- covid_demographics_norm_wide.df %>%
dplyr::filter(category == i) %>%
dplyr::select(-c(public_health_unit, category)) %>%
as.matrix()
# Set the rownames
rownames(temp_mx) <- mx_rownames
# Create a Heatmap object
hmap <- Heatmap(temp_mx[,-1], # Supply our matrix minus the populations size
# Use a viridis colourscale broken into 100 segments
col = viridis(100),
# Use column_title as the title of our heatmap
column_title = i,
# Rotate the legend horizontally and give it a title based on category
heatmap_legend_param = list(title = paste0(i, " per 100K individuals"),
legend_direction = "horizontal")
)
# Add this to our heatmap list
hm_list <- ...
}
# What kind of object is hm_list?
class(hm_list)
1.3.3 draw() your HeatmapList
Now that we have our HeatmapList we can simply draw the
whole thing and it will automatically concatenate for us. Of course
there are many options we can use to display the list. One thing to note
is that the clustering of columns in each heatmap is
independent while the clustering of rows (and thus
order) is based on only the first heatmap (by default).
Using the draw() method to display your
HeatmapList will allow you to further customize details of
the overall figure including setting row_title and
column_title for the entire figure.
draw(...,
column_title = "COVID-19 metrics breakdown by age group and PHU\n",
column_title_gp = gpar(fontsize = 20)
)
1.4.0 Building correlation heatmaps with complex RNA-Seq data
Our heatmap above is drawn from a relatively simple dataset but it
helps us to see that there are some strong signals that differentiate
between our different PHUs. It become clear that while the bulk of cases
in each PHU is dominated by the 20-39 age segment, the bulk of
hopsitalizations and deaths originate from the 80+ segment.
Of course, this data was cumulative information from January 2020 to
March 2022. With the aftermath of vaccinations and different variants, a
look at the current demographics may reveals a shift in these
trends.
Considering that the heatmap is a 34x7 grid, it is easier to
visualize and dissect all of the data in our dataset. What happens,
however, when we want to produce this kind of visualization from
something much larger or more complex?
Recall from last lecture that we investigated read count data from
Wyler et al., 2020 -
Wyler2020_AEC_SARSCoV2_17AAG_readcounts.tsv. We used this
dataset to produce a scatterplot matrix to compare between some of the
replicate data within the set. Across this set there are 36 sets of
RNA-Seq data spanning 27,011 genes.
Let’s begin by opening up the data file and getting a quick reminder
of what it looks like.
# Read in your read_count data
wyler_readcounts.df <- read_tsv(...)
str(wyler_readcounts.df, give.attr = FALSE)
1.4.1 Wrangle our readcount data
As you might recall from last week, there is quite a bit of data in
this set. So that we can more easily visualize our data, we’ll want to
wrangle it a bit more by:
- updating the column names to remove some of the unnecessary title
information.
- filtering by readcounts to limit our genes to those with values
between 1000 and 3000.
- Convert our dataframe to a matrix and rename the rows using the
genes.
Why do we filter our read counts? In this instance
we have chosen to filter our read counts. In some cases, you
might find wild swings in expression, and it is what we’re
looking for most of the time. For in-class analysis, to save a bit on
memory and time, we try to limit the read counts to a narrow set to
reduce our set size. It will also greatly reduce the size of our heatmap
for viewing. By filtering, we’ll go from 27,011 observations to 109.
wyler_readcounts_filtered.df <-
wyler_readcounts.df %>%
# Rename the columns be removing the first portion: AECII_xx
rename_with(., ~ str_replace(string = .x,
pattern = r"(\w*_\d*_)",
replace = "")) %>%
# filter out the low-readcount data
filter(if_all(.cols = -1, .fns = ~ .x > ... & .x < ...))
# Create our data matrix
wyler_readcounts.mx <- as.matrix(wyler_readcounts_filtered.df[, ...])
# Set the row names using the information from the data frame
rownames(wyler_readcounts.mx) <- wyler_readcounts_filtered.df$gene
# Check the characteristics of our matrix
head(wyler_readcounts.mx)
str(wyler_readcounts.mx)
1.4.2 Plot a heatmap of your readcount data
What if we were to produce a heatmap directly with the raw data? It
would be impossible to read, and including a dendrogram would actually
take forever to process given the 27,011 rows of data to process across
the 36 datasets. That’s why we’ll use our small subset of data as an
example to build a heatmap.
Let’s plot the data now with heatmap.2 and see how it
looks with a dendrogram.
# Create a Heatmap object
wyler_hmap <- Heatmap(..., # Supply our matrix
cluster_rows = TRUE, cluster_columns = TRUE, # Cluster on both rows and columns
col = ...,
# Use column_title as the title of our heatmap
column_title = "Heatmap of Wyler et al., 2020 readcount data",
# Rotate the legend horizontally and give it a title
heatmap_legend_param = list(title = "readcounts per gene",
legend_direction = "horizontal"),
)
# Plot the heatmap
draw(wyler_hmap,
# Plot the legend on the bottom
heatmap_legend_side = "bottom"
)
1.4.3 Generate a correlation matrix of your data
From the above output, we can see that our heatmap is already quite
hard to read and that’s coming off of using just 109 genes! We do,
however, get some strong trends regarding our datasets - we can see
certain sets of replicates are grouped together in the data but not all
of them. This might be clearer if we were to factor in all of the
datapoints or just the relevant genes that define the differences
between datasets. We’ll discuss the second part of that thought later in
this lecture.
Recall that what we’re really interested in, regarding these
readcount data, is to ask just how similar the experiments are between
each other. Whereas we were a little limited by the space needed to
produce the scatterplot matrix, we were able to produce correlation
values between experiments on a small scale. We can extend that
visualization forward to compare between all datasets and then
visually summarize the data as a heatmap.
The portion of the scatterplot matrix we’ll extend is the correlation
values. Whereas the ggpairs() function uses a Pearson
correlation, we can choose between Pearson or Spearman correlation.
Sometimes you may wish to compare both since Pearson examines linear
relationships whereas Spearman uses a ranking method to compare
variables for monotonic relationships.
To generate our matrix we’ll employ a couple of additional
functions:
expand.grid(): we can use this to build a matrix of
pair-wise combination values. We’ll use these to generate the pair-wise
column combinations we want to compare with cor().
apply(): by now you should be familiar with this
function. We’ll use it to iterate through our pair-wise column
combinations and send each of those to…
cor(): this will return the correlation co-efficient
based on the method we choose (pearson, kendall,
spearman)
# Example of how you can use expand.grid to make pairwise combination between two sets of data.
expand.grid(c(...),c(...))
# First we need to fix the column names from our data
wyler_readcounts_only.df <-
# Use the FULL set of readcount data
wyler_readcounts.df %>%
# Rename the columns be removing the first portion: AECII_xx
rename_with(., ~ str_replace(string = .x,
pattern = r"(\w*_\d*_)",
replace = "")) %>%
# Drop the first two columns as well
dplyr::select(c(3:38))
# Create our correlation matrix
wyler_cor.mx <-
# We'll apply a function to each row of the resulting grid pairs
matrix(apply(expand.grid(c(...),c(...)), # generate the pair-wise combinations
# explore it row-by-row
MARGIN = 1,
# Apply a function to each row which generates a pearson correlation between
# a specific pair of columns
function(x) cor(wyler_readcounts_only.df[, x[1]],
wyler_readcounts_only.df[, x[2]],
method = "pearson") # Use a Pearson correlation
), # End the apply function
nrow = 36, # Cast our result as a matrix
dimnames = list(colnames(wyler_readcounts_only.df), # name the rows and columns
colnames(wyler_readcounts_only.df))
)
# take a look at the final matrix
head(wyler_cor.mx)
1.4.4 Generate a heatmap of your correlation matrix
Now that we’ve completed the correlation matrix and it appears to be
correct, we can generate the heatmap of the data, allowing it to group
data based on the Pearson correlation values.
# Create a Heatmap object
wyler_hmap <-
Heatmap(wyler_cor.mx, # Supply our matrix
# Cluster on both rows and columns
cluster_rows = TRUE, cluster_columns = TRUE,
col = viridis(100),
# Use column_title as the title of our heatmap
column_title = "Heatmap of RNA-Seq Pearson correlation on readcounts",
# Rotate the legend horizontally and give it a title
heatmap_legend_param = list(title = "Pearson score",
legend_direction = "horizontal"),
# Set the row/column label font size
row_names_gp = gpar(fontsize = 16),
column_names_gp = gpar(fontsize = 16)
)
# Plot the heatmap
draw(wyler_hmap,
# Plot the legend on the bottom
heatmap_legend_side = "bottom"
)
1.5.0 Make a cluster dendrogram with hclust()
As you can see, clustering our data (on the right metric!) makes a
big difference in how our data is displayed. In our last few heatmaps,
we allowed Heatmap() to generate it’s own clustering. We
could have supplied it with a specific function to calculate distances,
or even a dendrogram object to order the data as well. Under the hood,
the function is defaulting to euclidean distances and actually passing
that information on to the hclust() function to produce the
dendrograms.
The choice of method determines how the data is
clustered. The choices include: ward.D, ward.D2, single, complete
(default), average, mcquitty, median and centroid.
In general ward.D, ward.D2
and complete strategies try to find compact small
“spherical” clusters. The single linkage adopts a ‘friends of friends’
clustering strategy. The other methods can be said to aim for somewhere
in between. For more information on these, you can dig into the hclust()
documentation
We can turn to hclust() to generate just dendrograms for
us but prior to that we still need to reformat our matrix a little using
the dist() function.
1.5.1 Calculate the distance between our points with
dist()
Remember we talked about our data being in n-dimensional space? Using
those coordinates, there are a number of ways to calculate the distance
between points. The simplest form is to calculate the euclidean distance
between points using the generic formula:
\[d(p,q) = \sqrt{(p_{1}-q_{2})^2 +
(p_{2}-q_{2})^2 + \ldots + (p_{n}-q_{n})^2}\]
but there are a number of other options as well. For instance you can
also choose the maximum distance between two components (same
dimension). The dist() function can generate these values
for you using the parameter method to determine how the
distance will be used. Your options are: euclidean, maximum, manhattan,
canberra, binary or minkowski.
The dist() function will return a dist
object which is a lower triangle distance matrix between all of the
rows/observations using the columns as coordinates.
Let’s return to our PHU data in
covid_demographics_norm.mx to try it out and see how it
works.
dist(covid_demographics_norm.mx[,-1],
method="euclidean") %>% str()# The default method is euclidean
1.5.2 Cluster your distance matrix
Now we are ready to cluster our distance matrix using the
hclust() method
Parameters that are important:
# Make a cluster object and take a look at it
phu.hc <- hclust(dist(covid_demographics_norm.mx[,-1]),
method = ...)
# What does our hclust object contain?
str(phu.hc)
2.0.0 Clustering data into groups
So we’ve visualized our dataset as a dendrogram and it gives us an
idea of how the samples might be related (in n-dimension space) based on
the case values we produced from normalization.
K-means clustering is unsupervised learning for uncategorized data.
We don’t really know the training labels of our data and are more
interested in seeing which ones group together. How many clusters should
we aim to generate? We’ve already seen that our data likely splits into
3 groups based on the heatmaps and hclust() data. Will we
get the same thing with a k-means method?
When in doubt, you can quickly consult the factoMiner
function fviz_nbclust() which can guess how many clusters
are ideal for representing your data. Note that it may not produce your
ideal number of clusters but if you’re stuck, this is a good start.
There are three method options: wss, gap_stat, and
silhouette which all aim to minimize the number of clusters.
wss elbow method aims to minimize intracluster
variation. How compact is our clustering? A lower WSS is
better.
silhouette measures how well an object lies within
it’s cluster by computing the average distance to other members in its
own, \(a(i)\) vs other clusters \(b(i)\). We evaluate \(\frac{b(i) - a(i)}{max\{a(i), b(i)}\) with
higher values indicating better clustering.
gap_stat also compares total intra-cluster variation
but against a null reference distribution. The optimal value attempts to
choose the number of clusters such that the smallest k gap
statistic is within one standard deviation of the gap at
k+1.
# How many clusters should we generate?
fviz_nbclust(x = covid_demographics_norm.mx[,-1], # Provide the dataset
FUNcluster = kmeans, # How will you cluster the data?
method=...) + # What method will you use?
theme(text = element_text(size=10))
# How many clusters should we generate?
fviz_nbclust(x = covid_demographics_norm.mx[,-1], # Provide the dataset
FUNcluster = kmeans, # How will you cluster the data?
method="silhouette") + # What method will you use?
theme(text = element_text(size=10))
# How many clusters should we generate?
fviz_nbclust(x = covid_demographics_norm.mx[,-1], # Provide the dataset
FUNcluster = kmeans, # How will you cluster the data?
method="gap_stat") + # What method will you use?
theme(text = element_text(size=10))
2.1.0 Generate your clusters using kmeans()
So from three different analyses we didn’t really get a concensus on
the best number of clusters:
wss: Our elbow level out at around k=3 or k=4
silhouette: Our biggest jump is at k=2 which is also our biggest
value
gap_stat: The biggest jump in within-cluster distance is at k=3
although we see another jump again at k=5
but it looks like the answer ranges between 2 and 5 clusters. Note
that if your data consistently suggest k = 1, then you shouldn’t cluster
at all! Since our data already suggests 3 clusters, let’s start with
that. We’ll use the kmeans() function to accomplish
this.
Similar in idea to hclust(), the kmeans()
algorithm attempts to generate k-partitioned groups from the data
supplied with an emphasis on minimizing the sum of squares from points
to the assigned cluster centers. This differs from hclust()
which finishes building the entire relationship via dendrogram without
actually choosing “clusters”.
We’re going to also use set.seed() for our random number
generation. We tend to think of things as random, but computationally,
randomness is built on algorithms. Therefore we can “recreate” our
randomness if we use a pre-determined starting point also known as the
“seed”.
The parameters we’re interested in using with kmeans()
are:
x the numeric matrix of our data
centers determines the number of clusters we want to
produce
nstart defines how many randomly chosen sets of k
centres you’ll use to start the analysis. Choosing multiple different
sets allows the algorithm to avoid local minima.
iter.max is the max number of iterations allowed
while trying to converge on the best minimum metric
# Compute k-means with k = 3
# Set a seed for reproducibility.
set.seed(123)
# Generate our k-means analysis
phu_cases.km <-
kmeans(..., # We'll scale our data for this (more on that later too!)
centers = 3,
nstart = 25,
iter.max = 500)
# What is the structure of our kmeans object?
str(phu_cases.km)
# K-means clusters showing the group of each individuals
phu_cases.km$...
2.2.0 Plot your k-means results with
fviz_cluster()
Notice the structure of this output? It includes cluster
which denotes which row belongs to each of the 3 clusters chosen. The
centre of each cluster is defined by centers which in this
case is a 3x7 array where each row uses 7 values to define their
position within our 7-dimension dataset. We can see each cluster’s
size is 18, 11, and 5 PHUs respectively. Notice, however,
that none of the original coordinates for our data are retained in this
object.
Rather than use ggplot directly, we’ll use the
ggplot-compatible fviz_cluster() function to help us
transform the values of our points from a 7-dimension coordinate system
to a 2-dimension visual format suitable for our simpler brains. Under
the hood fviz_cluster() is performing a principle component
analysis to group our data along the first two principal
components (more on what that means later too!).
The parameters we should be concerned with in this function
include:
object the partitioning object for our data. In the
case of k-means, it is our kmeans object.
data is the original dataset we used to generate the
data. This will be necessary to plot the other data points.
ellipse.type determines how we’ll outline our
clusters. This comes with a number of options including:
- convex: draws boundaries based on the outer points in your
cluster
- confidence: produces a confidence ellipse around the cluster
centres. This can be further adjusted using the parameter
ellipse.level whose default is 0.95.
- t, norm: assume multivariate t-distribution and multivariate normal
distributions to produce their ellipses using
ellipse.level
to set their radius.
- euclid: sets a circle of radius
ellipse.level around
the centre of each cluster.
Let’s see how our data has been partitioned by the k-means
clustering.
# Plot the cluster
# You can treat this object like a ggplot object afterwards
phu_kmeans.plot <-
fviz_cluster(object = ..., # our k-means object
data = ..., # Our original data needed for PCA to visualize
ellipse.type = "convex",
ggtheme = theme_bw(),
repel=TRUE, # Try to avoid overlapping text
labelsize = 20,
pointsize = 4,
main = "K-means clustering of PHU by normalized case number"
) +
# Set some ggplot theme information
theme(text = element_text(size=20)) +
# Set the colour and fill scheme to viridis
scale_fill_viridis_d() +
scale_colour_viridis_d()
# Print the plot!
phu_kmeans.plot
2.3.0 Clustering your RNA-Seq data
Let’s return to the Wyler et al., 2020 readcount data and
take a look at it through a different lens. Whereas before we had
generated a heatmap of the data based on looking at genes expressed
within a readcount range, we’ll now take a different approach.
Let’s look at the top 500 variable genes in the dataset to help
cluster them. To accomplish that we’ll want to generate the standard
deviation in read count for each row (gene). We’ll utilize a few verbs
we haven’t used before in this class:
rowwise(): in the same class of functions as
group_by(), this will subtly alter the tibble so that when
we perform math operations on it, these will be completed on a
row-by-row basis.
c_across(): this helper version of combine
(c()) combines values across columns within our
tibble.
# Review the wyler readcount data
head(wyler_readcounts.df)
# Save our results into a new dataframe
wyler_readcounts_filtered.df <-
# Start with the original read count data
wyler_readcounts.df %>%
# Rename the columns by removing the first portion: AECII_xx
rename_with(., ~ str_replace(string = .x,
pattern = r"(\w*_\d*_)",
replace = "")) %>%
# filter out the low readcount data. Low values will create wild variance easily
filter(if_all(.cols = -1, .fns = ~ .x > 10)) %>%
# Prepare to do row-wise calculations
... %>%
# Calculate the standard deviation across rows
mutate(stdev = sd(...)) %>%
# Ungroup and sort the data by descending value
ungroup() %>%
arrange(desc(stdev)) %>%
# Take the top 500 most variable genes
dplyr::slice(1:500)
Now we’ll just convert our dataframe into a matrix of values so we
can perform our various analyses.
# Save just the RNA-Seq data into a matrix
wyler_readcounts.mx <- as.matrix(wyler_readcounts_filtered.df[, 3:38])
# Set the row names using the information from the data frame
rownames(wyler_readcounts.mx) <- wyler_readcounts_filtered.df$gene
# Take a quick look at the resulting matrix and its properties
head(wyler_readcounts.mx)
str(wyler_readcounts.mx)
2.3.1 Create a heatmap of the most variable genes
Since we’re here, let’s take a quick step back and look at the
heatmap from our new dataset. Does it reveal anthing to us now that it
is more nuanced?
# Create a Heatmap object
wyler_hmap <-
Heatmap(..., # Supply our matrix
cluster_rows = TRUE, cluster_columns = TRUE, # Cluster on both rows and columns
col = viridis(100),
# Use column_title as the title of our heatmap
column_title = "Heatmap of RNA-Seq readcounts on top 500 most variable genes",
# Rotate the legend horizontally and give it a title
heatmap_legend_param = list(title = "readcounts per gene",
legend_direction = "vertical"),
# Remove the row names
show_row_names = FALSE,
# Set the dendrogram sizes
... = unit(40, "mm"),
... = unit(20, "mm")
)
# Plot the heatmap
draw(wyler_hmap,
# Plot the legend on the bottom
heatmap_legend_side = "left"
)
2.3.2 Generate a k-means cluster analyis
Looks like the heatmap still doesn’t reveal a lot of information to
us like how samples might be grouped. Let’s proceed with k-means and see
if that works better. We just need to repeat our steps on a different
set of data. Since we now have our most diverse genes and their
readcounts, we’ll take a look at if we can cluster this information.
- Generate an estimate on the appropriate number of clusters
- Create our k-means cluster object
- Visualize the k-means object and see which experiments tend to group
together.
# How many clusters should we generate?
fviz_nbclust(t(wyler_readcounts.mx), FUNcluster = kmeans, method="wss")
fviz_nbclust(t(wyler_readcounts.mx), FUNcluster = kmeans, method="silhouette")
fviz_nbclust(t(wyler_readcounts.mx), FUNcluster = kmeans, method="gap_stat")
From the 3 methods we see that
Our WSS method produced an elbow around k = 4
Our silhouette method peaks at k = 4
Our gap_stat method sees diminishing progress also at k =
4
Let’s proceed with that in mind!
# Compute k-means with k = 4
# Set a seed for reproducibility.
set.seed(123)
# Generate our k-means analysis
wyler_readcounts.km <-
kmeans(scale(...), # We'll scale our data for this (more on that later too!)
centers = 4,
nstart = 25,
iter.max = 500)
# Take a look at the resulting k-means object
str(wyler_readcounts.km)
# Plot the cluster
# You can treat this object like a ggplot object afterwards
wyler_kmeans.plot <-
fviz_cluster(object = ..., # our k-means object
data = t(wyler_readcounts.mx), # Our original data needed for PCA to visualize
ellipse.type = "convex",
ggtheme = theme_bw(),
repel=TRUE, # Try to avoid overlapping text
labelsize = 20,
pointsize = 4,
main = "K-means clustering of filtered Wyler readcount data"
) +
# Set some ggplot theme information
theme(text = element_text(size=20)) +
# Set the colour and fill scheme to viridis
scale_fill_viridis_d() +
scale_colour_viridis_d()
# Print the plot!
wyler_kmeans.plot
2.3.3 Interpreting our RNA-Seq clustering
Looking at the result, what’s nice about this kind of output is that
we can immediately see the separation or clustering of our data. It
looks like 4 was the way to go as there are 4 tight groupings from our
data. It might be possible to further subdivide the group in the top
left of our figure but it’s unlikely.
Based on our selection of genes, we observe:
24-, 48-, and 72-hour samples mock-treated (DMSO) and
mock-infected, cluster together with 24-hour mock-treated samples
infected with SARS-CoV-2.
48- and 72-hour samples mock-treated (DMSO) and infected by
SARS-CoV-2 appear to share a similar profile.
24-hour samples treated with 200 nM 17AAG (HSP90 inhibitor)
cluster together whether or not they are infected with
SARS-CoV-2.
48- and 72-hour samples treated with 200 nM 17AAG cluster
together whether or not they are infected with SARS-CoV-2.
These groupings suggest that perhaps the 24-hour SARS-CoV-infected
timepoint is similar to uninfected controls. Meanwhile the 48- and
72-hour SARS-CoV-infected samples are similar but likely showing very
distinct transcriptional changes due to infection. Treatment with the
HSP90-inhibitor, however, appears to sufficiently alter expression
profiles of infected and mock-infected cells to makes them cluster
together. This would be a good starting point to begin digging further
into the data.
Skeptic or believer? While our above analysis seems
to make sense to us, it lacks a deeper understanding of what might be
happening. For one thing, we haven’t even looked closely at the genes
used to create our clustering. How many of them are relevant to the
infection process? Is the clustering due to off-target effects of the
HSP90 inhibitor (17AAG)? While clustering is an effective way to quickly
identify if subgroups exist within your data, it’s important to follow
up these findings with in-depth analyses of the data.

Yes the data clusters, but why does it cluster?
3.0.0 Dimension reduction trims the (data) fat
One problem we encountered in our above analysis of RNA-Seq data is
that there were simply too many dimensions! With ~27k gene entries in
our dataframe, using clustering to analyse the entire dataset would be
memory-intensive if not impossible altogether. To circumvent this
problem we attempted to subset our data in two ways - filtering by read
counts, and comparing variance across datasets. These approaches were a
form of dimensionality reduction - namely
feature elimination. Our choices, however, may have inadvertently been
throwing away data that could prove insightful! This is where other
dimensionality reduction methods can help guide our analyses.
You can achieve dimensionality reduction in three general ways:
Feature elimination: you can reduce the feature
space by removing unhelpful features. No information is gained but you
trim down your dataset size.
Feature selection: on the reverse side you can
simply choose the most important features to you. How will you decide?
Some schemes include ranking your features by importance but this may
suffer from information loss if incorrect features are chosen.
Feature extraction: create new independent
features which are a combination of the old features! Attempt to extract
the essential features of your data in a more informationaly dense
manner.
| Random forest |
Popular machine learning algorithm for classification
randomly chooses from subsets of features to classify data. The most
frequently appearing features amongst the forests are chosen. |
Feature selection |
Only takes numeric inputs |
| PCA |
Principal component analysis attempts to maximize
variation when transforming to a lower-dimensional space. All new
features are independent of one another. |
Linear Feature Extraction |
Actually really good at finding outliers in RNAseq
data |
| t-SNE |
t-Distributed Stochastic Neighbour Embedding is a
non-linear technique similar to PCA suitable for high-dimension
datasets. It attempts to minimize probabilities in mirroring nearest
neighbour relationships in transforming from high to low-dimension
spaces |
Non-linear Feature extraction |
Determine if your data has underlying structure |
| UMAP |
Uniform manifold approximation and projection is
projection-based like t-SNE, this technique attempts to preserve local
data structure but has improved translation of global data
structure. |
Non-linear feature extraction |
Faster than t-SNE |
Since we’re not exactly building a classifier but rather trying to
find trends in our data, we won’t be looking at Random Forests here.
Here we are interested in exploratory data analysis. We have a
data set we want to understand, sometimes it is too complex to just
project or divine 1) the underlying structure and 2) the features that
drive that structure.
There’s more to explore with dimension reduction:
The above table is just a small subset of potentially different kinds of
dimension reduction methods. PCA for instance has 2 “variants”: Multiple
Correspondence Analysis (MCA) which deals with relationships between
categorical variables rather than continuous ones, and Independent
Component Analysis (ICA) which also uses linear dimension reduction to
identify independent components in your dataset. You can check out a
little more here
and over
here
3.1.0 Reduce your dimensionality with PCA
Going back to our PHU data, we used 7 dimensions to classify our data
but what if we wanted to transform that information in some way to
reduce the number of dimensions needed to represent their relationships?
For our data set, 7 dimensions isn’t a lot of features but in other
cases (like RNA-Seq) you might encounter feature-dense datasets and
without knowing a priori which ones are meaningful, PCA
provides a path forward in classifying our data.
Now there are caveats to PCA. It produces linear feature extraction
by building new features in your n-dimensional space such that each new
feature (kind of like a projected line) maximizes variance to the
original data points. Each new component must be uncorrelated with (ie
perpendicular to) the previous ones while accounting for the next
highest amount of variance. More resources on how this works in the
references.

How do we go about maximizing the variance (spread of red dots) to
our data features? Finding the point where variance is maximized, also
minimizes error (red line lengths). Generated by user
Amoeba on stackexchange.com
All math aside, our goal is to reduce our feature set to something
smaller by trying to represent our data with these new features. Just
remember that highly variable data and outliers can dominate your
principal components.
For simplicity let’s head back and look at our PHU age group data
again. To illustrate our example with PCA, let’s use the original data
normalized by PHU population.
# View the normalized PHU age group data
head(covid_demographics_norm.mx)
3.2.0 Use PCA() to generate our analysis
To generate our analysis of the PHU data, we’ll use the
FactoMineR function PCA() for which there are
some parameters we’ll be using that we should discuss: - X
a data frame of n rows (observations) and p columns
(numeric variables) - ncp the number of dimensions kept in
the results (5 is the default) - scale.unit a boolean to
scale your data (TRUE by default)
3.2.1 What does it mean to scale our data?
Remember that PCA is trying to maximize distance between a principle
component and all of the observations supplied. Depending on the nature
of your variables you may have, for instance, two different unit types
like height and mass. Smaller changes in height may be matched with much
larger changes in mass or just wider overall variance. This may lead the
PCA algorithm to prioritize mass over height when you’d prefer they have
an equal importance. By centering your mean and scaling data to unit
variance, everything is compared as a z-score, bringing
the overall variance across a variable to within ~3 standard
deviations.
Let’s compare PCA with and without scaling shall we?
# Build a PCA of our PHU data with scaling applied
phu_scaled.pca <- PCA(...,
scale.unit = ..., # What happens when we don't scale the data?
ncp = ...,
graph = TRUE)
# Build a PCA of our PHU data WITHOUT scaling applied
phu_unscaled.pca <- PCA(covid_demographics_norm.mx[,-1],
scale.unit = ...,
ncp = 7,
graph = TRUE)
# Take a look at the information inside our PCA object
print(...)
3.2.2 Our PCA object has a complex number of data pieces
Regardless of scaling, the result of our PCA() call
produces an object with many variables we can access. Above you can see
a brief description for each variable but we are most interested in a
few particular ones:
eig holds our dimensional data but also describes
just how much each new principle component describes the overall
variation of our data.
var holds the results of all the variables. We can
use these to graph and visualize our data.
ind$coord will allow us to plot the coordinates of
our observations along the principal components.
3.3.0 Use the eigenvalues to determine the percent variance of each
component
The eigenvalues from our analysis pair with the eigenvectors
(principle components) to help transform our data from the original
feature set to the new set of features. While the eigenvectors may
determine the directions of the new feature space, the
eigenvalue represents the magnitude of the
vector and in this case can be used to calculate the percent of overall
variance explained by our eigenvector.
The important take-away is that we can now see just how much of our
variance is explained in each new principle component. We can access
this information directly from phu_scaled.pca or by using
the function get_eigenvalue(). We can also plot this as a
barchart instead using fviz_eig().
# Look at our eigenvalues directly
phu_scaled.pca$...
# Use get_eigenvalue() to look at our eigenvalues
get_eigenvalue(phu_scaled.pca)
3.3.1 Build a scree plot to look at the effect of your
dimensions
See how nearly 80% of our variation is explained in just our first
dimension? Let’s use fviz_eig() to display this information
visually in what is known as a scree plot. It’s essentially a
barplot/lineplot combo but what we’re interested in is following the
lines much like our cluster-estimating WSS method. We use a “sharp
elbow” to determine how many principal components we need.
# Visualize the impact of our eigenvalues
fviz_eig(..., addlabels = TRUE) + theme(text = element_text(size=10))
3.3.2 Most of our variance is accounted for in the first two
principle components!
Looking at our eigenvalues we can see that even though 7 new PCs were
generated, the first two explain almost 92% of our variance. That
suggests that whatever linear separation in our data exists, we
can recreate in a two-dimensional projection of coordinates from PC1 and
PC2. This suggests that we can recreate the underlying structure of our
data with these two new features instead of using a 7-dimensional
space!
This makes some sense when we take a closer look at the data. For
instance, there isn’t much visual information found in our
0 to 4 age range. The small distribution of information in
these features may not do much, in the grand scheme, to change how the
PHUs are separated from each other. Then again, perhaps they do
contain information that we simply cannot see easily! How will we
know?
3.4.0 Investigate your variables with
get_pca_var()
Within our PCA, we can access information regarding how our original
variables are transformed into the new space. This is all stored in the
var element of our PCA object. We can extract the aspects
of this using the get_pca_var() and visualize these to
determine the quality of our variables and their representation by the
new principal components.
# What is the information associated with our original variables
phu.var <- get_pca_var(...)
phu.var
3.4.1 What is the contribution of each variable to the new
components?
Each original variable may, to some degree, influence the amount of
information captured into each new principal component. When we look at
each we can see the breakdown of variables, which in some cases, can
reveal to us where more or less variation is found as well.
# What is the contribution of each variable?
phu.var$...
From above we can see that our original variables equally contribute
to our first principal component but there is much more contribution in
PC2 from three variables: 0 to 4, 5 to 11 and 80+. From our previous
scree plot that means that 12.8% of overall variation, which is
described in PC2, is mainly from these three groups.
How many dimensions should I get and how many do I
keep? It is at this point that we should discuss the
maximum number of possible dimensions. Given
n observations, and
p features, the maximum number of dimensions
after reduction is min(n-1, p). In terms of
how many dimensions you keep, you should consider the general rule of
thumb that at least 80% of your variation
should be accounted for by the principal components that you choose.
Keep that in mind!
3.4.2 Checking the quality and representation of your variables in
the PCA with coord and cor
From the remaining variable information, coord and
cor can both be used to plot our original variables along
different pairs of principal components. These values are the same
because this is not a projection of our variables onto the PCs
but a correlation of them! The distance between the origin and the
variable coordinates gauges the quality of the variables on the map.
This number is summarized in the cos2 (squared cosine)
element.
# Look at the coordinates of our variables across the PCs.
phu.var$...
phu.var$...
# What is the quality of our variables?
# The higher the value the better!
phu.var$...
3.5.0 Check if PHUs cluster or separate with
fviz_pca_ind()
Now that we’ve generated our PCA, let’s see if there is any clear
separation between our PHUs across the first two dimensions of new
features. Much like our variables, all of the individual coordinate data
can be found in our ind element of our PCA object. Within
there, we’ll find the coord information so we could
directly build a ggplot with phu_scaled.pca$ind$coord BUT
there’s a simpler way.
We’ll parse through the PCA object with fviz_pca_ind()
to plot our data along the first two principle coordinates. We can
define pointsize and col.ind to help add some
population size information to our PHUs.
# Graph our scaled PCA data.
test <-
fviz_pca_ind(...,
pointsize = covid_demographics_norm.mx[,1], # Set point size and colour by PHU population
col.ind = log10(covid_demographics_norm.mx[,1]),
repel = TRUE, # avoid overlapping text points
labelsize = 5,
axes = c(1,2)
) +
theme(text = element_text(size=10)) + # Make our text larger
scale_size(range = c(3, 10)) + # Update the point size range
scale_colour_viridis_c() # Change the colour scheme
test
# Graph our UNscaled PCA data.
fviz_pca_ind(...,
pointsize = covid_demographics_norm.mx[,1], # Set point size and colour by PHU population
col.ind = log10(covid_demographics_norm.mx[,1]),
repel = TRUE, # avoid overlapping text points
labelsize = 5,
axes = c(1,2)
) +
theme(text = element_text(size=10)) + # Make our text larger
scale_size(range = c(3, 10)) + # Update the point size range
scale_colour_viridis_c() # Change the colour scheme
# What are the contributions of each age group to the various unscaled dimensions
phu_unscaled.pca$var$contrib
# Look at our unscaled eigenvalues directly
phu_unscaled.pca$eig
3.5.1 Scaled vs unscaled data
As you can see from our graphing of both PCA sets, when we choose not
to scale our data something a little more drastic happens. PC1’s portion
of variance increases to a 83.2% accounting of overall variance. We see
many of our points move and separation between some of our PHUs is
increased (Kingston/Frontenac and Peel). These changes are likely due to
the larger range of values from the 20 to 39 age group which now
contributes to 31% of PC1’s variation.
So think carefully about your features and what they represent.
Depending on their range, and unit values, you may be inadvertently
weighing them more than your other features! Scaling adjusts your data
so that all features are weighted equally!
3.7.0 PCA on a large RNA-Seq dataset
Now that we’ve walked through how to generate a PCA for a simpler
dataset, let’s return to our RNA-Seq readcount data. We won’t trim down
the data at all but rather just provide the entire dataset as a matrix
for feature reduction. Let’s review the steps:
- Generate a matrix of your data, naming the columns and rows by
whatever information you might have. Make sure your columns represent
your features, and rows are observations.
- Create the PCA object.
- Review your eigenvalues and eigenvectors to assess how well your
reduction worked. Determine how many dimensions you wish to use in
further analysis.
- Calculate the number of possible clusters.
- Plot the results.
# 1. Generate our matrix from the readcount data
wyler_readcounts_all.mx <-
wyler_readcounts.df %>%
# Rename the columns by removing the first portion: AECII_xx
rename_with(., ~ str_replace(string = .x,
pattern = r"(\w*_\d*_)",
replace = "")) %>%
# Keep only the experimental observations (how many are there?)
dplyr::select(c(3:38)) %>%
# Convert to a matrix
as.matrix() %>%
# Transpose the matrix so our columns are now genes
...
# Name the rows of the matrix
colnames(wyler_readcounts_all.mx) <- wyler_readcounts.df$gene
# We'll peek at the structure of the matrix. Looking at it with head() will be messy.
str(wyler_readcounts_all.mx)
# 2. Generate the PCA object
readcounts_scaled.pca <- PCA(...,
scale.unit = TRUE, # What happens when we don't scale the data?
ncp = 40,
graph = TRUE)
# 3. Review how much variance is explained by the new components
readcounts_scaled.pca$eig
# 4. How many clusters should we use based on our PCA data?
fviz_nbclust(readcounts_scaled.pca$ind$coord[,1:2], FUNcluster = kmeans, method="silhouette")
# 5. Generate our k-means analysis and visualize it
set.seed(123)
readcounts_PCA_kmeans.plot <-
# Let's go with 4 clusters in order to compare to our original version.
# This is close to the silhouette value as well.
kmeans(readcounts_scaled.pca$ind$coord[,1:6], centers = ..., ) %>%
fviz_cluster(., # our k-means object
data = readcounts_scaled.pca$ind$coord[,1:2], # Our original data needed for PCA to visualize
ellipse.type = "convex",
ggtheme = theme_bw(),
repel=TRUE, # Try to avoid overlapping text
labelsize = 20,
pointsize = 4,
main = "K-means clustering of PHU by normalized case number AFTER Principal Component Analysis"
) +
# Set some ggplot theme information
theme(text = element_text(size=10)) +
# Set the colour and fill scheme to viridis
scale_fill_viridis_d() +
scale_colour_viridis_d()
# Look at our original plot with 4 centres
wyler_kmeans.plot
# Versus our PCA with 4 centres
readcounts_PCA_kmeans.plot
If we were to compare our two versions of the k-means clustering
using raw data vs our PCA dimension-reduced data, the results are pretty
similar although not quite. I’ve also added what your data would look
like if you did NOT scale the data before performing PCA.
| Scaled |
Scale at time of k-means |
Scale at time of PCA |
Scale at time of PCA |
Unscaled at time of PCA |
| Features used for k-means |
27K genes |
Top 2 components (43% var) |
Top 6 components (67% var) |
Top 2 components (73 % var) |
| Dim 1 |
44.6% |
26.1% |
26.1% |
50.3% |
| Dim 2 |
17.8% |
16.9% |
16.9% |
22.7% |
| Cluster 1 |
24h: all, DMSO-treated;
48h+72h: uninfected, DMSO |
24h+48h: SARS-CoV2 infection,
DMSO;
Some 24h+48h: uninfected, DMSO |
24h: all, DMSO-treated;
48h+72h: uninfected, DMSO |
24h: all, DMSO-treated;
48h+72h: uninfected, DMSO |
| Cluster 2 |
48h+72h: SARS-CoV2 infection, DMSO-treated |
48h: uninfected, DMSO;
72h: all, DMSO-treated |
48h+72h: SARS-CoV2 infection, DMSO-treated |
48h+72h: SARS-CoV2 infection, DMSO-treated |
| Cluster 3 |
24h: 17AAG-treated |
24h: 17AAG-treated |
24h: 17AAG-treated |
24h: 17AAG-treated |
| Cluster 4 |
48h+72h: 17AAG-treated |
48h+72h: 17AAG-treated |
48h+72h: 17AAG-treated |
48h+72h: 17AAG-treated |
Some interesting conclusions we can draw from this are
- Whether our data is scaled just prior to performing k-means, scaled
or unscaled with PCA, we see that our 17AAG-treated samples have 2 main
clusters regardless of infection status. Either 24h or 48h+72h data.
There is likely a strong effect on expression by the 17AAG
treatment that is much stronger than the infection by
SARS-CoV2. In fact, if we were to explore this further we would see that
if we created more clusters, we would find that
time plays a bigger role in further separating
these groups than SARS-CoV2 infection.
- Choosing the number of features does matter even after PCA! Looking
at just our scaled PCA data to create a k-mean clustering, we can’t get
good concordance with our raw data analysis using only our
top two PCA features which account for just
43% of variance. On the other hand, if we
choose our top six features for k-means
clustering which account for 67% of variance,
we do get the same clusters returned, albeit a slighlty different
looking project.
Therefore PCA can help identify factors which heavily influence your
samples as long as you also correctly choose enough features to
represent your transformed data.
3.8.0 Restart R!
At this point, we’ve accumulated a lot of data in memory. We won’t
need our previous data anymore so let’s restart R and reload the
libraries we’ll need. You can
# Packages to help tidy our data
library(tidyverse)
library(readxl)
library(magrittr)
# Packages for the graphical analysis section
library(viridis)
library(RColorBrewer)
# Data projection packages
library(Rtsne)
library(umap)
4.0.0 Non-linear projection

How do we identify trends or groups within deeply complex data in an
unsupervised manner?
So we just spent most of our time trying to understand how to
transform our sets based on the large amounts of variation in our data.
There are some limitations we’ve discussed but in some cases, depending
on your dataset size you may be interested in finding small, local
similarities rather than the large variation that comes with PCA.
There are two popular projections we’ll discuss today but first let’s
put together a more complex dataset based on some RNAseq data in
GSE150316_DeseqNormCounts_final.txt and it’s companion file
2020.07.30.20165241-supp_tables.xlsx
# Read in our RNAseq data
tissue_data.df <- ...(file = ...,
header = TRUE,
row.names = 1)
# Take a quick look at it
head(tissue_data.df)
dim(tissue_data.df)
# Read in some additional patient data
patient_data.df <- read_excel("./data/2020.07.30.20165241-supp_tables.xlsx", sheet=2)
# Take a quick look at it
head(patient_data.df)
dim(patient_data.df)
4.0.1 Why can’t we use PCA on this dataset?
Before we attempt one of our non-linear projection methods, let’s try
and use some of the methods we’ve spent all this time looking at. We’ll
naively repeat our PCA steps on this tissue data and see if we can pull
any information from it. We know there are 10 tissue groups in our data
so we’ll use that as a basis for clustering.
As we can see from the above plot, a majority of our samples from
various tissue types fall into a single cluster. What’s more, as we’ll
see, there is some structure in these samples as multiple samples come
from the same patient. Sometimes even the same tissue! These
relationships may have a large effect in the overall variation we see in
the data, making it hard to distinguish between even tissue types. This
is why we’ll turn to non-linear projection and see if it can help us
out. Before that we’ll need to do some more wrangling…
4.0.3 Prepare our RNAseq tissue data for analysis by eliminating
features
We now want to format tissue_data.df much in the way we
did with our PHU data. We want to convert our current data which lists
genes as observations and tissue samples as columns. Essentially, we’d
like to transpose this and we could do that but the
transposition converts everything to a matrix. In the end, we want to
work with a data frame so we can hold more information.
To reduce on memory and runtime, however,
we should trim our dataset. We aren’t really interested in looking at
59,090 genes - many of which may be barely expressed. Since these are
normalized counts across the dataset, we can filter out low-expression
genes to make tissue_data_filtered.df. Yes, this would
again be considered a form of feature elimination. In general
we will:
- Convert the row names to their own column.
- Calculate the mean of the normalized counts across each observation
and filter for a minimum of 0.5.
- Calculate the variance across each observation as a backup
plan.
system.time(
# Trim the tissue data down
tissue_data_filtered.df <-
tissue_data.df %>%
# Convert the row names to an actual column
rownames_to_column(var="gene") %>%
# Set up the table to perform row-wise operations
rowwise() %>%
# Calculate the mean expression of each gene across all tissue samples
mutate(mean = ...(c_across(where(is.numeric)))) %>%
# Filter for samples with low expression
filter(mean > 0.5) %>%
# Calculate overall variance in case we need to make our dataset smaller
mutate(variance = ...(c_across(where(is.numeric)))) %>%
# Arrange samples by descending variance
arrange(desc(variance)) %>%
# Remove the grouping specification
ungroup()
)
# Take a look at the final results
head(tissue_data_filtered.df)
# how big is our filtered data frame?
dim(tissue_data_filtered.df)
4.0.4 More data wrangling to transpose our RNAseq data
Now that we’ve filtered our data down to ~29k genes, we’ll run
through two more steps:
- We’ll transpose our data using a combination of
pivot_longer() and pivot_wider().
- We’ll use a series of string matches and joins to add some sample
information to our data. It won’t be used for our analysis but will be
used when we plot the results!
- Lastly we’ll convert the relevant parts of our data frame to a
matrix. There is a huge memory savings in the algorithm when working
with a matrix and you have limited memory on your RStudio Hubs!
# You need to transpose the data.
# We can do it with dplyr to keep it as a data frame and to add some info
tissue_RNAseq.df <-
tissue_data_filtered.df %>%
# trim down the columns to drop mean/variance
dplyr::select(1:89) %>%
# pivot longer
pivot_longer(cols=c(2:89), names_to = ..., values_to = ...) %>%
# redistribute the gene names to columns
pivot_wider(names_from = gene, values_from = ...)
# We want to add some additional sample information before assessing the data
tissue_RNAseq.df <-
tissue_RNAseq.df %>%
# Grab just the sample names
dplyr::select(sample) %>%
# Grab information from it like case number, tissue, and tissue number
# takes the form of "caseX.tissueY" or "caseX.tissue.NYC" or "NegControlX"
# Remember that this returns a LIST of character matrices
str_match_all(., pattern=r"(case([\w]+)\.([a-z]+)([\d|\.NYC]*)|(NegControl\d))") %>%
# Bind all the matrices from all the list elements together in a single object (likely a matrix)
do.call(rbind, .) %>%
# Convert the results to a data frame
as.data.frame() %>%
# Rename the columns based on the capture groups
dplyr::rename(., sample = V1, case_num = V2, tissue = V3, tissue_num = V4, neg_num = V5) %>%
# Coalesce some of the info due to negative control samples and clean up a column
mutate(case_num = coalesce(case_num, neg_num),
tissue_num = str_replace_all(.$tissue_num, pattern = "\\.", replace = "")) %>%
# Drop the neg_num column
dplyr::select(1:4) %>%
# Join this result to the RNAseq info
full_join(., y=tissue_RNAseq.df, by=c("sample" = "sample")) %>%
# Join that result to grab viral load information
right_join(patient_viral_load.df, y=., by=c("case_num" = "case_num"))
# Look at the resulting dataframe
head(tissue_RNAseq.df)
# How many tissue types do we have?
table(tissue_RNAseq.df$tissue)
# Generate a matrix version of our data but drop the sample metadata!
tissue_RNAseq.mx <- as.matrix(tissue_RNAseq.df[,...])
# head(tissue_RNAseq.mx)
str(tissue_RNAseq.mx)
dim(tissue_RNAseq.mx)
4.1.0 t-Distributed Stochastic Neighbour Embedding with the
Rtsne package
We now have a somewhat more complex dataset. We are still short on
actual samples (now observations) but 88 observations and nearly 30K
features isn’t so bad. A broad question we may wish to ask with such a
data set is if there is an underlying structure to these samples -
i.e. do we see grouping based on tissue type, or perhaps even sample
preparation.
t-Distributed Stochastic Neighbour Embedding or
t-SNE is a way for us to project our high-dimension
data onto a lower dimension with the aim at preserving the local
similarities rather than global disparity. When looking at data
points, t-SNE will attempt to preserve the local neighbouring structure.
As the algorithm pours over the data, it can use different
transformations for different regions as it attempts to transform
everything to a lower dimension. It is considered “incredibly flexible”
at finding local structure where other algorithms may not.
This flexibility is accomplished through 2 steps:
- Reduce dimensionality of your data features with PCA!
- Generate a probability distribution between all pairs by making
similar objects highly probably and assigning dissimilar objects a low
probability.
- Define a similar probability distribution for the samples in a lower
dimension while minimizing the divergence between the two distributions
based on a distance metric between points in the lower dimension.
We’ll discuss more about how this algorithm affects interpretation
after seeing the results, but this is considered an
exploratory data visualization tool, rather
than explanatory.
To produce our t-SNE projection we’ll use the Rtsne()
function from the package of the same name. Some important parameters
are:
X is our data matrix where each row is an
observation
dims sets the number of dimensions we’d like to
project onto (default is 2).
initial_dims sets the number of dimensions that
should be retained in the initial PCA step (Default 50).
perplexity a numeric parameter that tunes between
local and global aspects of your data.
This parameter is a guess as to how many close neighbours a point
may have. If you have a sense of sample types (or clusters!) ahead of
time, you could try to play with this value (default is 30).
According to the algorithm this value should follow this rule:
\(perplexity * 3 \lt nrow(X)
-1\)
pca_scale is a boolean to set if the initial PCA
step should use scaled data.
max_iter is the maximum number of iterations in the
algorithm (default is 1000).
# Rtsne prefers using a matrix for memory issues
# set a seed for reproducible results
set.seed(2024)
# Try for perplexity of 30 can go as high as 29 before crash
# We have just 90 samples, but between 1-52 samples per "group"
tissue_tsne <- Rtsne(...,
dims=2,
perplexity=5,
verbose=TRUE,
pca_scale = TRUE,
max_iter = 1500)
# What does our t-SNE object look like?
str(...)
4.1.2 Interpreting our t-SNE plot
While we don’t have a lot of samples, you can still see that we were
able to cluster some of our data by cell types without
providing that classification to the algorithm! Great job team!
We can see that we get close clustering of tissues like placenta,
heart, and bowel. Our liver samples are kind of everywhere but perhaps
using a different perplexity would provide different results.
One interesting thing we can see is that regardless of tissue type,
we see some samples are clustering together based on case number -
namely case numbers 1, 4 and 5 seem to have some strong underlying
expression profiles that connect them across tissue samples. We may also
be seeing false relationships so beware!
5.0.0 Class summary

While t-SNE and UMAP produce projections to produce clustered data,
you have no route back to understanding their relationships. PCA, on the
other hand, is strictly a dimension reduction tool. It does not place or
assign datapoints to any groups BUT it is useful to use on large
datasets prior to clustering!
Today we took a deep dive into principal component analysis. There
are of course different variants of this based on the assumptions you
can make about your observations and variables like independent
component analysis (ICA, non-Gaussian features) and multiple
correspondence analysis (MCA, categorical features). Some additional
methods can also be used to store the transformation like a PCA does,
notably variational autoencoders (VAE).
Overall we should remember that while PCA can have problems in
generating it’s feature extraction, it is deterministic and
repeatable. Also, the final results are provided in such a
way that new observations could be transformed and projected
onto the same principal components. You can also feed these components
back into clustering algorithms like k-means to try and identify
specific subgroups.
t-SNE and UMAP, on the other hand appear to do a much better job with
high-dimensional data. They can preserve local structure and UMAP can
also do a fairly good job of preserving global structure. These tools
make for great exploratory analysis of your complex datasets.
Interpretation of relationships, however, are not mathematically clear
like in PCA. These are, after all projections from a higher dimension
for our simpler primate brains!
5.1.0 Weekly assignment
This week’s assignment will be found under the current lecture folder
under the “assignment” subfolder. It will include an R markdown notebook
that you will use to produce the code and answers for this week’s
assignment. Please provide answers in markdown or code cells that
immediately follow each question section.
| Code |
50% |
- Does it follow best practices? |
|
|
- Does it make good use of available packages? |
|
|
- Was data prepared properly |
| Answers and Output |
50% |
- Is output based on the correct dataset? |
|
|
- Are groupings appropriate |
|
|
- Are correct titles/axes/legends correct? |
|
|
- Is interpretation of the graphs correct? |
Since coding styles and solutions can differ, students are encouraged
to use best practices. Assignments may be rewarded for
well-coded or elegant solutions.
You can save and download the markdown notebook in its native format.
Submit this file to the the appropriate assignment section by 12:59 pm
on the date of our next class: April 18th, 2024.
5.2.0 Acknowledgements
Revision 1.0.0: created and prepared for
CSB1021H S LEC0141, 03-2021 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
Revision 1.0.1: edited and prepared for
CSB1020H S LEC0141, 03-2022 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
Revision 1.0.2: edited and prepared for
CSB1020H S LEC0141, 03-2023 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
Revision 2.0.0: Revised and prepared for
CSB1020H S LEC0141, 03-2024 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
The Center for the Analysis of Genome Evolution and Function
(CAGEF)
The Centre for the Analysis of Genome Evolution and Function (CAGEF)
at the University of Toronto offers comprehensive experimental design,
research, and analysis services in microbiome and metagenomic studies,
genomics, proteomics, and bioinformatics.
From targeted DNA amplicon sequencing to transcriptomes, whole
genomes, and metagenomes, from protein identification to
post-translational modification, CAGEF has the tools and knowledge to
support your research. Our state-of-the-art facility and experienced
research staff provide a broad range of services, including both
standard analyses and techniques developed by our team. In particular,
we have special expertise in microbial, plant, and environmental
systems.
For more information about us and the services we offer, please visit
https://www.cagef.utoronto.ca/.
LS0tDQp0aXRsZTogJycNCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCiMgVGhpcyBhbGxvd3MgdGhlIGZpbGUgdG8gYmUgTElWRSBhbmQgcnVuIHdpdGhvdXQgZXJyb3JzIHN0b3BwaW5nIGl0Lg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVycm9yID0gVFJVRSkNCmBgYA0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L0NBR0VGX3NlcnZpY2VzX3NsaWRlLnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KOjo6DQoNCiMgQWR2YW5jZWQgR3JhcGhpY3MgYW5kIERhdGEgVmlzdWFsaXphdGlvbiBpbiBSDQoNCiMgTGVjdHVyZSAwNTogRGltZW5zaW9uIFRyYW5zZm9ybWF0aW9uIExJVkUgSFRNTCB3YWl0aW5nIA0KDQojIyAwLjEuMCBBbiBvdmVydmlldyBvZiBBZHZhbmNlZCBHcmFwaGljcyBhbmQgRGF0YSBWaXN1YWxpemF0aW9uIGluIFINCg0KKioiQWR2YW5jZWQgR3JhcGhpY3MgYW5kIERhdGEgVmlzdWFsaXphdGlvbiBpbiBSIioqIGlzIGJyb3VnaHQgdG8geW91IGJ5IHRoZSBDZW50cmUgZm9yIHRoZSBBbmFseXNpcyBvZiBHZW5vbWUgRXZvbHV0aW9uICYgRnVuY3Rpb24ncyAoQ0FHRUYpIGJpb2luZm9ybWF0aWNzIHRyYWluaW5nIGluaXRpYXRpdmUuIFRoaXMgQ1NCMTAyMSB3YXMgZGV2ZWxvcGVkIHRvIGVuaGFuY2UgdGhlIHNraWxscyBvZiBzdHVkZW50cyB3aXRoIGJhc2ljIGJhY2tncm91bmRzIGluIFIgYnkgZm9jdXNpbmcgb24gYXZhaWxhYmxlIHBoaWxvc29waGllcywgbWV0aG9kcywgYW5kIHBhY2thZ2VzIGZvciBwbG90dGluZyBzY2llbnRpZmljIGRhdGEuIFdoaWxlIHRoZSBkYXRhc2V0cyBhbmQgZXhhbXBsZXMgdXNlZCBpbiB0aGlzIGNvdXJzZSB3aWxsIGJlIGNlbnRyZWQgb24gU0FSUy1Db1YtMiBlcGlkZW1pb2xvZ2ljYWwgYW5kIGdlbm9taWMgZGF0YSwgdGhlIGxlc3NvbnMgbGVhcm5lZCBoZXJlaW4gd2lsbCBiZSBicm9hZGx5IGFwcGxpY2FibGUuDQoNClRoaXMgbGVzc29uIGlzIHRoZSBmaWZ0aCBpbiBhIDYtcGFydCBzZXJpZXMuIFRoZSBhaW0gZm9yIHRoZSBlbmQgb2YgdGhpcyBzZXJpZXMgaXMgZm9yIHN0dWRlbnRzIHRvIHJlY29nbml6ZSBob3cgdG8gaW1wb3J0LCBmb3JtYXQsIGFuZCBkaXNwbGF5IGRhdGEgYmFzZWQgb24gdGhlaXIgaW50ZW5kZWQgbWVzc2FnZSBhbmQgYXVkaWVuY2UuIFRoZSBmb3JtYXQgYW5kIHN0eWxlIG9mIHRoZXNlIHZpc3VhbGl6YXRpb25zIHdpbGwgaGVscCB0byBpZGVudGlmeSBhbmQgY29udmV5IHRoZSBrZXkgbWVzc2FnZShzKSBmcm9tIHRoZWlyIGV4cGVyaW1lbnRhbCBkYXRhLg0KDQpUaGUgc3RydWN0dXJlIG9mIHRoZSBjbGFzcyBpcyBhICoqY29kZS1hbG9uZyBzdHlsZSoqIGluIFIgbWFya2Rvd24gbm90ZWJvb2tzLiBBdCB0aGUgc3RhcnQgb2YgZWFjaCBsZWN0dXJlLCBza2VsZXRvbiB2ZXJzaW9ucyBvZiB0aGUgbGVjdHVyZSB3aWxsIGJlIHByb3ZpZGVkIGZvciB1c2Ugb24gdGhlIFtVbml2ZXJzaXR5IG9mIFRvcm9udG8gZGF0YXRvb2xzIEh1Yl0oaHR0cHM6Ly9kYXRhdG9vbHMudXRvcm9udG8uY2EpIHNvIHN0dWRlbnRzIGNhbiBwcm9ncmFtIGFsb25nIHdpdGggdGhlIGluc3RydWN0b3IuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAwLjIuMCBMZWN0dXJlIG9iamVjdGl2ZXMNCg0KVGhpcyB3ZWVrIHdpbGwgZm9jdXMgb24gZXhwbG9yaW5nIHRoZSByZWxhdGlvbnNoaXBzIHdpdGhpbiB5b3VyIGRhdGEgb2JzZXJ2YXRpb25zIGNvbXBhcmluZyBiZXR3ZWVuIGV4cGVyaW1lbnRhbCBzYW1wbGVzIGFuZCBhcHBseWluZyBhIHN1aXRlIG9mIGNsdXN0ZXIsIGRpbWVuc2lvbnMgcmVkdWN0aW9uIGFuZCBwcm9qZWN0aW9uIGFsZ29yaXRobXMuDQoNCkF0IHRoZSBlbmQgb2YgdGhpcyBsZWN0dXJlIHlvdSB3aWxsIGhhdmUgY292ZXJlZCB2aXN1YWxpemF0aW9ucyByZWxhdGVkIHRvDQoNCjEuICBDbHVzdGVyaW5nIHdpdGggaGVhdG1hcHMgYW5kIGRlbmRyb2dyYW1zDQoyLiAgSy1tZWFucyBjbHVzdGVyaW5nDQozLiAgUHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcw0KNC4gIE5vbi1saW5lYXIgcHJvamVjdGlvbnMNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDAuMy4wIEEgbGVnZW5kIGZvciB0ZXh0IGZvcm1hdCBpbiBSIG1hcmtkb3duDQoNCmBncmV5IGJhY2tncm91bmRgIC0gYSBwYWNrYWdlLCBmdW5jdGlvbiwgY29kZSwgY29tbWFuZCBvciBkaXJlY3RvcnkuIEJhY2t0aWNrcyBhcmUgYWxzbyB1c2UgZm9yIGluLWxpbmUgY29kZS5cDQoqaXRhbGljcyogLSBhbiBpbXBvcnRhbnQgdGVybSBvciBjb25jZXB0IG9yIGFuIGluZGl2aWR1YWwgZmlsZSBvciBmb2xkZXJcDQoqKmJvbGQqKiAtIGhlYWRpbmcgb3IgYSB0ZXJtIHRoYXQgaXMgYmVpbmcgZGVmaW5lZFwNCltibHVlIHRleHRde3N0eWxlPSJjb2xvcjpibHVlIn0gLSBuYW1lZCBvciB1bm5hbWVkIGh5cGVybGluaw0KDQpgLi4uYCAtIFdpdGhpbiBlYWNoIGNvZGluZyBjZWxsIHRoaXMgd2lsbCBpbmRpY2F0ZSBhbiBhcmVhIG9mIGNvZGUgdGhhdCBzdHVkZW50cyB3aWxsIG5lZWQgdG8gY29tcGxldGUgZm9yIHRoZSBjb2RlIGNlbGwgdG8gcnVuIGNvcnJlY3RseS4NCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC1pbmZvfQ0KKipCbHVlIGJveDoqKiBBIGtleSBjb25jZXB0IHRoYXQgaXMgYmVpbmcgaW50cm9kdWNlZA0KOjo6DQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtd2FybmluZ30NCioqWWVsbG93IGJveDoqKiBSaXNrIG9yIGNhdXRpb24NCjo6Og0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LXN1Y2Nlc3N9DQoqKkdyZWVuIGJveGVzOioqIFJlY29tbWVuZGVkIHJlYWRzIGFuZCByZXNvdXJjZXMgdG8gbGVhcm4gUg0KOjo6DQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtZGFuZ2VyfQ0KKipSZWQgYm94ZXM6KiogQSBjb21wcmVoZW5zaW9uIHF1ZXN0aW9uIHdoaWNoIG1heSBvciBtYXkgbm90IGludm9sdmUgYSBjb2RpbmcgY2VsbC4gWW91IHVzdWFsbHkgZmluZCB0aGVzZSBhdCB0aGUgZW5kIG9mIGEgc2VjdGlvbi4NCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMC40LjAgTGVjdHVyZSBhbmQgZGF0YSBmaWxlcyB1c2VkIGluIHRoaXMgY291cnNlDQoNCiMjIyAwLjQuMSBXZWVrbHkgTGVjdHVyZSBhbmQgc2tlbGV0b24gZmlsZXMNCg0KRWFjaCB3ZWVrLCBuZXcgbGVzc29uIGZpbGVzIHdpbGwgYXBwZWFyIHdpdGhpbiB5b3VyIFJTdHVkaW8gZm9sZGVycy4gV2UgYXJlIHB1bGxpbmcgZnJvbSBhIEdpdEh1YiByZXBvc2l0b3J5IHVzaW5nIHRoaXMgW1JlcG9zaXRvcnkgZ2l0LXB1bGwgbGlua10oaHR0cHM6Ly9yLmRhdGF0b29scy51dG9yb250by5jYS9odWIvdXNlci1yZWRpcmVjdC9naXQtcHVsbD9yZXBvPWh0dHBzJTNBJTJGJTJGZ2l0aHViLmNvbSUyRmNhbW9rJTJGMjAyNC0wMy1BZHZfR3JhcGhpY3NfUiZ1cmxwYXRoPXJzdHVkaW8lMkYmYnJhbmNoPW1haW4pLiBTaW1wbHkgY2xpY2sgb24gdGhlIGxpbmsgYW5kIGl0IHdpbGwgdGFrZSB5b3UgdG8gdGhlIFtVbml2ZXJzaXR5IG9mIFRvcm9udG8gZGF0YXRvb2xzIEh1Yl0oaHR0cHM6Ly9kYXRhdG9vbHMudXRvcm9udG8uY2EpLiBZb3Ugd2lsbCBuZWVkIHRvIHVzZSB5b3VyIFVUT1JpZCBjcmVkZW50aWFscyB0byBjb21wbGV0ZSB0aGUgbG9naW4gcHJvY2Vzcy4gRnJvbSB0aGVyZSB5b3Ugd2lsbCBmaW5kIGVhY2ggd2VlaydzIGxlY3R1cmUgZmlsZXMgaW4gdGhlIGRpcmVjdG9yeSBgLzIwMjQtMDMtQWR2X0dyYXBoaWNzX1IvTGVjdHVyZV9YWGAuIFlvdSB3aWxsIGZpbmQgYSBwYXJ0aWFsbHkgY29kZWQgYHNrZWxldG9uLlJtZGAgZmlsZSBhcyB3ZWxsIGFzIGFsbCBvZiB0aGUgZGF0YSBmaWxlcyBuZWNlc3NhcnkgdG8gcnVuIHRoZSB3ZWVrJ3MgbGVjdHVyZS4NCg0KQWx0ZXJuYXRpdmVseSwgeW91IGNhbiBkb3dubG9hZCB0aGUgUi1NYXJrZG93biBOb3RlYm9vayAoYC5SbWRgKSBhbmQgZGF0YSBmaWxlcyBmcm9tIHRoZSBSU3R1ZGlvIHNlcnZlciB0byB5b3VyIHBlcnNvbmFsIGNvbXB1dGVyIGlmIHlvdSB3b3VsZCBsaWtlIHRvIHJ1biBpbmRlcGVuZGVudGx5IG9mIHRoZSBUb3JvbnRvIHRvb2xzLg0KDQojIyMgMC40LjIgTGl2ZS1jb2RpbmcgSFRNTCBwYWdlDQoNCkEgbGl2ZSBsZWN0dXJlIHZlcnNpb24gd2lsbCBiZSBhdmFpbGFibGUgYXQgW2NhbW9rLmdpdGh1Yi5pb10oaHR0cHM6Ly9jYW1vay5naXRodWIuaW8vMjAyNC0wMy5BZHZfR3JhcGhpY3NfUi9pbmRleC5odG1sKSB0aGF0IHdpbGwgdXBkYXRlIGFzIHRoZSBsZWN0dXJlIHByb2dyZXNzZXMuIEJlIHN1cmUgdG8gcmVmcmVzaCB0byB0YWtlIGEgbG9vayBpZiB5b3UgZ2V0IGxvc3QhDQoNCiMjIyAwLjQuMyBQb3N0LWxlY3R1cmUgUERGcw0KDQpBcyBtZW50aW9uZWQgYWJvdmUsIGF0IHRoZSBlbmQgb2YgZWFjaCBsZWN0dXJlIHRoZXJlIHdpbGwgYmUgYSBjb21wbGV0ZWQgdmVyc2lvbiBvZiB0aGUgbGVjdHVyZSBjb2RlIHJlbGVhc2VkIGFzIGEgUERGIGZpbGUgdW5kZXIgdGhlIE1vZHVsZXMgc2VjdGlvbiBvZiBRdWVyY3VzLg0KDQojIyMgMC40LjQgRGF0YSB1c2VkIGluIHRoaXMgbGVzc29uDQoNClRvZGF5J3MgZGF0YXNldHMgd2lsbCBmb2N1cyBvbiB0aGUgc21hbGxlciBkYXRhc2V0cyB3ZSB3b3JrZWQgb24gaW4gZWFybGllciBsZWN0dXJlcyAobmFtZWx5IG91ciBPbnRhcmlvIHB1YmxpYyBoZWFsdGggdW5pdCBDT1ZJRC0xOSBkZW1vZ3JhcGhpY3MgZGF0YSksIGFuZCBhIG5ldyBzZXQgb2YgUk5Bc2VxIGFuYWx5c2lzIG9uIGRpZmZlcmVudCB0aXNzdWUgc2FtcGxlcyBmcm9tIENPVklELTE5IHBhdGllbnRzDQoNCiMjIyAwLjQuNC4xIERhdGFzZXQgMTogcGh1X2RlbW9ncmFwaGljc19jZW5zdXNfbm9ybS5jc3YNCg0KVGhpcyBpcyBhIGNvbWJpbmF0aW9uIG9mIGRhdGFzZXRzIGZyb20gcHJldmlvdXMgbGVjdHVyZXMuIFRoaXMgaW5jb3Jwb3JhdGVzIFBIVSBkZW1vZ3JhcGhpYyBkYXRhIHdpdGggU3RhdHNDYW4gY2Vuc3VzIGRhdGEgZnJvbSAyMDE3IHRvIHByb2R1Y2UgYSBub3JtYWxpemVkIGVzdGltYXRlIG9mIGNhc2VzLCBkZWF0aHMsIGFuZCBob3NwaXRhbGl6YXRpb25zIGFjcm9zcyBhZ2UgZ3JvdXBzIGFuZCBwdWJsaWMgaGVhbHRoIHVuaXRzIGluIE9udGFyaW8uDQoNCiMjIyAwLjQuNC4yIERhdGFzZXQgMjogV3lsZXIyMDIwX0FFQ19TQVJTQ29WMl8xN0FBR19yZWFkY291bnRzLnRzdg0KDQpUaGlzIGlzIHRoZSBzYW1lIHJlYWRjb3VudCBkYXRhIHdlIGxvb2tlZCBhdCBpbiBMZWN0dXJlIDA0LiBSTkEtU2VxIHJlYWQgY291bnQgZGF0YSBnZW5lcmF0ZWQgZnJvbSBTQVJTLUNvVjIgaW5mZWN0aW9ucyBvZiBBRUMgY2VsbHMuIFVzZWQgdG8gY29tcGFyZSB0aGUgdGltZWNvdXJzZSBvZiBleHByZXNzaW9uIChwcm8taW5mbGFtbWF0b3J5KSBjaGFuZ2VzIGluIHNhbXBsZXMgdHJlYXRlZCB3aXRoIGFuZCB3aXRob3V0IEhTUDkwIGluaGliaXRvcnMuIFB1Ymxpc2hlZCBpbiAqaVNjaWVuY2UqIGRvaTogPGh0dHBzOi8vZG9pLm9yZy8xMC4xMDE2L2ouaXNjaS4yMDIxLjEwMjE1MT4NCg0KIyMjIDAuNC40LjMgRGF0YXNldCAzOiBHU0UxNTAzMTZfRGVzZXFOb3JtQ291bnRzX2ZpbmFsLnR4dA0KDQpGcm9tIERlc2FpIGV0IGFsLiwgMjAyMCBvbiBtZWRSeGl2IGRvaTogPGh0dHBzOi8vZG9pLm9yZy8xMC4xMTAxLzIwMjAuMDcuMzAuMjAxNjUyNDE+IHRoaXMgZGF0YXNldCBoYXMgbm9ybWFsaXplZCBleHByZXNzaW9uIGNvdW50cyBmcm9tIFJOQXNlcSBkYXRhLiBJdCBjb3ZlcnMgbXVsdGlwbGUgc2FtcGxlcyBhbmQgdGlzc3VlcyBmcm9tIENPVklELXBvc2l0aXZlIHBhdGllbnRzIHdpdGggYSBmb2N1cyBvbiBsdW5nIHRpc3N1ZS4gVGhlIGV4cHJlc3Npb24gZGF0YSBoYXMgYmVlbiB1bmZpbHRlcmVkIGZvciBTQVJTLUNvVi0yIGV4cHJlc3Npb24gZGF0YSBhcyB3ZWxsLg0KDQojIyMgMC40LjQuNCBEYXRhc2V0IDQ6IDIwMjAuMDcuMzAuMjAxNjUyNDEtc3VwcF90YWJsZXMueGxzeA0KDQpGcm9tIERlc2FpIGV0IGFsLiwgMjAyMCBvbiBtZWRSeGl2IGRvaTogPGh0dHBzOi8vZG9pLm9yZy8xMC4xMTAxLzIwMjAuMDcuMzAuMjAxNjUyNDE+IHRoaXMgZGF0YXNldCBjb250YWlucyBwYXRpZW50IGluZm9ybWF0aW9uIGxpa2UgdmlyYWwgbG9hZCBmcm9tIHRpc3N1ZXMgdGhhdCB3ZXJlIHVzZWQgZm9yIFJOQXNlcS4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDAuNS4wIFBhY2thZ2VzIHVzZWQgaW4gdGhpcyBsZXNzb24NCg0KYHRpZHl2ZXJzZWAgd2hpY2ggaGFzIGEgbnVtYmVyIG9mIHBhY2thZ2VzIGluY2x1ZGluZyBgZHBseXJgLCBgdGlkeXJgLCBgc3RyaW5ncmAsIGBmb3JjYXRzYCBhbmQgYGdncGxvdDJgDQoNCmB2aXJpZGlzYCBoZWxwcyB0byBjcmVhdGUgY29sb3ItYmxpbmQgcGFsZXR0ZXMgZm9yIG91ciBkYXRhIHZpc3VhbGl6YXRpb25zDQoNCmBSQ29sb3JCcmV3ZXJgIGhhcyBzb21lIGhsZXBmdWwgcGFsZXR0ZXMgdGhhdCB3ZSdsbCBuZWVkIHRvIGNvbG91ciBvdXIgZGF0YS4NCg0KYGdwbG90c2Agd2lsbCBiZSB1c2VkIHRvIGhlbHAgZ2VuZXJhdGUgaGVhdG1hcC9kZW5kcm9ncmFtIHZpc3VhbGl6YXRpb25zLg0KDQpgRmFjdG9NaW5lUmAgYW5kIGBmYWN0b2V4dHJhYCB3aWxsIGJlIHVzZWQgZm9yIFBDQSBnZW5lcmF0aW9uIGFuZCB2aXN1YWxpemF0aW9uDQoNCmBSdHNuZWAgYW5kIGB1bWFwYCBhcmUgcGFja2FnZXMgaW1wbGVtZW50aW5nIG5vbi1saW5lYXIgcHJvamVjdGlvbiBhbGdvcml0aG1zDQoNCmBgYHtyfQ0KIyBTb21lIHBhY2thZ2VzIGNhbiBiZSBpbnN0YWxsZWQgdmlhIEJpb2NvbmR1Y3Rvcg0KaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpDQoNCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJDb21wbGV4SGVhdG1hcCIpDQoNCmluc3RhbGwucGFja2FnZXMoIkZhY3RvTWluZVIiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KaW5zdGFsbC5wYWNrYWdlcygiZmFjdG9leHRyYSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQoNCmluc3RhbGwucGFja2FnZXMoIlJ0c25lIikNCmluc3RhbGwucGFja2FnZXMoInVtYXAiKQ0KDQojIC0tLS0tLS0tLS0tLSBMZWdhY3kgaW5zdGFsbGF0aW9uIGNvZGUgd2hpY2ggd2UgaG9wZWZ1bGx5IG5ldmVyIG5lZWQgYWdhaW4gLS0tLS0tLS0tLSMNCiMgV2UgbmVlZCB0byBzcGVjaWZpY2FsbHkgaW5zdGFsbCBhIHBhY2thZ2UgY2FsbGVkIHBia3J0ZXN0IGZvciB0aGUgZmFjdG8gcGFja2FnZXMNCiMgVGhpcyBpcyBkdWUgdG8gdXNpbmcgYW4gb2xkZXIgdmVyaW9uIG9mIFIgDQojIGxpYnJhcnkocmVtb3RlcykNCiMgaW5zdGFsbF92ZXJzaW9uKCJwYmtydGVzdCIsICIwLjUuMSIpDQoNCmBgYA0KDQpgYGB7cn0NCiMgUGFja2FnZXMgdG8gaGVscCB0aWR5IG91ciBkYXRhDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShtYWdyaXR0cikNCg0KIyBQYWNrYWdlcyBmb3IgdGhlIGdyYXBoaWNhbCBhbmFseXNpcyBzZWN0aW9uDQpsaWJyYXJ5KHZpcmlkaXMpDQojIGxpYnJhcnkoZ3Bsb3RzKSAjIGhlYXRtYXAyKCkNCmxpYnJhcnkoQ29tcGxleEhlYXRtYXApICMgSGVhdG1hcCgpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCg0KIyBVc2VmdWwgZm9yIFBDQSBhbmQgUENBIHZpc3VhbGl6YXRpb24NCmxpYnJhcnkoRmFjdG9NaW5lUikNCmxpYnJhcnkoZmFjdG9leHRyYSkNCg0KIyBEYXRhIHByb2plY3Rpb24gcGFja2FnZXMNCmxpYnJhcnkoUnRzbmUpDQpsaWJyYXJ5KHVtYXApDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgMS4wLjAgRGF0YSBjYXRlZ29yaXphdGlvbiwgZGltZW5zaW9uIHJlZHVjdGlvbiwgYW5kIGRpbWVuc2lvbiB0cmFuc2Zvcm1hdGlvbg0KDQpMYXN0IHdlZWsgd2UgbG9va2VkIGF0IGFuIGFuYWx5c2lzIG9mIFJOQXNlcSBkYXRhIHRocm91Z2ggYSBudW1iZXIgb2YgbWV0aG9kcyBzdGFydGluZyB3aXRoIGJyb2FkLWxldmVsIHZvbGNhbm8gcGxvdHMgYW5kIG1vdmluZyB0b3dhcmRzIGdlbmUtbGV2ZWwgZXhwcmVzc2lvbiB2aXN1YWxpemF0aW9ucyB3aXRoIGRvdHBsb3RzLiBJbiBiZXR3ZWVuIHdlIHN0b3BwZWQgdG8gdGFrZSBhIGxvb2sgYXQgaGVhdG1hcHMuIEluIHRoaXMgaW5zdGFuY2Ugd2Ugc2ltcGx5IHVzZWQgaGVhdG1hcHMgdG8gY29udmV5IGV4cHJlc3Npb24gbGV2ZWxzIG9mIG11bHRpcGxlIGdlbmVzIGFjcm9zcyBvbmUgb3IgbW9yZSBzYW1wbGVzLg0KDQpMb29raW5nIG1vcmUgYnJvYWRseSwgd2Ugbm93IHdpc2ggdG8gYXNrIHF1ZXN0aW9ucyBzdWNoIGFzICJob3cgKnNpbWlsYXIqIGFyZSBvdXIgc2FtcGxlcz8iLCAiY2FuIG91ciBzYW1wbGVzIGJlIGdyb3VwZWQgb3IgY2F0ZWdvcml6ZWQgaW4gc29tZSB3YXk/IiBhbmQgImlzIHRoZXJlIGFuIHVuZGVybHlpbmcgc3RydWN0dXJlIG9yIGFyY2hpdGVjdHVyZSB0byBvdXIgZGF0YT8iIFdlIGJyaWVmbHkgZGlzY3Vzc2VkICoqKnNjYXR0ZXJwbG90IG1hdHJpY2VzKioqIHRvIGhlbHAgYW5hbHlzZSBvdXIgc2FtcGxlIHF1YWxpdHkgYW1vbmdzdCByZXBsaWNhdGUgZXhwZXJpbWVudHMuDQoNCldlIGNhbiBzdHVkeSB0aGVzZSBxdWVzdGlvbnMgdXNpbmcgYSBudW1iZXIgb2YgdGVjaG5pcXVlcyB0aGF0IHJhbmdlIGZyb20gY2x1c3RlcmluZy9jYXRlZ29yaXphdGlvbiB0byBwcm9qZWN0aW9uIG9mIGhpZ2gtZGltZW5zaW9uYWwgZGF0YSBvbnRvIGEgbG93ZXIgc2V0IG9mIGRpbWVuc2lvbnMgKHVzdWFsbHkgMiBvciAzKS4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDEuMS4wIFdoYXQga2luZCBvZiBkYXRhIGRvIHdlIGNhcmUgYWJvdXQ/DQoNCldlIGNhbm5vdCBiZWdpbiBvdXIgam91cm5leSB1bnRpbCB3ZSB0YWxrIGFib3V0IHRoZSBuYXR1cmUgb2YgdGhlIGRhdGEgd2UgYXJlIGludGVyZXN0ZWQgaW4gZXhhbWluaW5nLiBVc3VhbGx5LCBvdXIgZGF0YSB3aWxsIGNvbnNpc3Qgb2YgbWFueSBzYW1wbGVzIChvYnNlcnZhdGlvbnMpIHdpdGggc29tZSBudW1iZXIgb2YgZmVhdHVyZXMgY2FwdHVyZWQgYWJvdXQgZWFjaCBvYnNlcnZhdGlvbi4gRm9yIGV4YW1wbGUsIHdpdGggUk5Bc2VxIGRhdGEgd2UgY291bGQgY29uc2lkZXIgdGhlIG1lYXN1cmVtZW50cyB3ZSBjYXB0dXJlIGZvciAqZWFjaCogZ2VuZSBhcyBhIHNlcGFyYXRlIGZlYXR1cmUvdmFyaWFibGUvY29sdW1uLg0KDQpDb252ZXJzZWx5IHlvdSBtYXkgaGF2ZSBodW5kcmVkcyBvciB0aG91c2FuZHMgb2Ygc2FtcGxlcyB5b3UnZCBsaWtlIHRvIGNhdGVnb3JpemUgaW4gc29tZSB3YXkgKG9yIHNob3cgdGhhdCB5b3UgY2FuIGNhdGVnb3JpemUpIHdpdGgganVzdCBhIHNtYWxsZXIgc2V0IG9mIGZlYXR1cmVzLiBGb3IgZXZlcnkgbnV0LCB0aGVyZSBpcyBhIHRvb2wgKm9mIHNvcnRzKiBmb3IgY3JhY2tpbmcgaXQhDQoNCldlJ2xsIHN0YXJ0IHdpdGggYSBtb2RpZmllZCB2ZXJzaW9uIG9mIHRoZSBQSFUgYWdlIGdyb3VwIGRhdGFzZXQgdGhhdCB3ZSd2ZSBzZWVuIGJlZm9yZSBmcm9tIExlY3R1cmUgMDIgYW5kIDAzLiBUaGUgZGVtb2dyYXBoaWNzIGRhdGEgaGFzIGJlZW4gbW9kaWZpZWQgdG8gaW5jbHVkZSBhIHNldCBvZiBub3JtYWxpemVkIHZhbHVlcyAoaW5kaXZpZHVhbHMgcGVyIDEwMGspIHRoYXQgd2FzIGNhbGN1bGF0ZWQgYmFzZWQgb24gU3RhdHNDYW4gMjAxNyBjZW5zdXMgZGF0YS4gTW9kZWxpbmcgb2ZmIG9mIHRoZXNlIGRhdGEsIHRoZSAyMDIyIHNpemVzIGZvciAqKmVhY2ggYWdlIGdyb3VwKiogd2VyZSBlc3RpbWF0ZWQuIENhc2UsIGRlYXRoLCBhbmQgaG9zcGl0YWxpemF0aW9uIGNvdW50cyB3ZXJlIG5vcm1hbGl6ZWQgYmFzZWQgb24gdGhlc2Ugc3ViZ3JvdXAgc2l6ZXMgdG8gZ2VuZXJhdGUgdmFsdWVzIHBlciAxMDBLIGluZGl2aWR1YWxzLiBXZSdsbCBiZSB3b3JraW5nIHdpdGggdGhpcyAyMDIyIGRhdGFzZXQgbWFpbmx5IGJlY2F1c2UgaXQgdXRpbGl6ZXMgbW9yZSBmaW5lLWdyYWluZWQgYmlubmluZyBvZiBvdXIgYWdlLWdyb3VwcyB0aGFuIG1vcmUgcmVjZW50IGRhdGFzZXQgZnJvbSB0aGUgT250YXJpbyBnb3Zlcm5tZW50IGFyY2hpdmVzLg0KDQpUaGUgdXBkYXRlZCBkYXRhc2V0IHdpbGwgYmUgZm91bmQgaW4gYHBodV9kZW1vZ3JhcGhpY3NfY2Vuc3VzX25vcm0uY3N2YCBhbmQgd2UnbGwgdXNlIGl0IHRvIGd1aWRlIHVzIHRocm91Z2ggdHdvIHNlY3Rpb25zIG9mIHRvZGF5J3MgbGVjdHVyZS4NCg0KTGV0J3MgYmVnaW4gYnkgbG9hZGluZyB0aGUgZGF0YSBhbmQgdGFraW5nIGEgbG9vayBhdCBpdHMgc3RydWN0dXJlLg0KDQpgYGB7cn0NCiMgSW1wb3J0IHRoZSBub3JtYWxpemVkIGRlbW9ncmFwaGljcyBkYXRhDQpjb3ZpZF9kZW1vZ3JhcGhpY3Nfbm9ybS5kZiA9IHJlYWRfY3N2KC4uLikNCg0KIyBMb29rIGF0IGl0J3Mgc3RydWN0dXJlDQpzdHIoY292aWRfZGVtb2dyYXBoaWNzX25vcm0uZGYsIGdpdmUuYXR0ciA9IEZBTFNFKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLjIuMCBMb29raW5nIGZvciB0cmVuZHMvZ3JvdXBzIGluIHlvdXIgZGF0YQ0KDQpMZXQncyBiZWdpbiBsb29raW5nIGF0IG91ciBgY292aWRfZGVtb2dyYXBoaWNzX25vcm0uZGZgLiBVc2luZyBhIHNlcmllcyBvZiBkYXRhIHNldHMsIHdlJ3ZlIGNyZWF0ZWQgYSBjb25zb2xpZGF0ZWQgZGF0YXNldCB3aXRoOg0KDQoxLiAgQ3VtdWxhdGl2ZSBjYXNlcywgZGVhdGhzLCBhbmQgaG9zcGl0YWxpemF0aW9ucyBkdWUgdG8gQ09WSUQtMTkgd2l0aGluIGVhY2ggYWdlIGdyb3VwIHBlciBQSFUuDQoNCjIuICBSZXByZXNlbnRhdGlvbiBvZiBlYWNoIGFnZSBncm91cCB3aXRoaW4gZWFjaCBkYXRhIGNhdGVnb3J5IGFzIGEgcGVyY2VudCBvZiB0b3RhbCBpbmNpZGVudHMgaW4gZWFjaCBQSFUuDQoNCjMuICBVc2luZyAyMDE3IGNlbnN1cyBkYXRhLCB0aGUgbnVtYmVyIG9mIGNhc2VzIHBlciAxMDAsMDAwIGluZGl2aWR1YWxzIG5vcm1hbGl6ZWQgYnkgZXN0aW1hdGVkIHBvcHVsYXRpb24gc2l6ZSBmb3IgZWFjaCBhZ2UgZ3JvdXAgd2l0aGluIGVhY2ggUEhVLg0KDQpUaGUgcXVlc3Rpb24gd2Ugd2FudCB0byBhbnN3ZXIgaXM6IG9mIHRoZSAzNCBwdWJsaWMgaGVhbHRoIHVuaXRzLCB3aGljaCBsb29rIG1vc3Qgc2ltaWxhciBiYXNlZCBvbiB0aGUgKioqbm9ybWFsaXplZCBjYXNlIGRhdGEqKiogZm9yIGVhY2ggYWdlIGdyb3VwPyBJbiBvcmRlciB0byB2aXN1YWxpemUgdGhpcyBkYXRhLCB3ZSdsbCB3YW50IHRvIGNvbnZlcnQgb3VyIGN1cnJlbnQgbG9uZy1mb3JtIGRhdGEgdGhhdCBsb29rcyBzb21ldGhpbmcgbGlrZSB0aGlzOg0KDQp8IHB1YmxpY19oZWFsdGhfdW5pdCB8IGFnZV9ncm91cCB8IHRvdGFsX2Nhc2VzIHwgLi4uIHwgY2FzZXNfcGVyXzEwMGsgfCBkZWF0aHNfcGVyXzEwMGsgfCBob3NwaXRhbGl6YXRpb25zX3Blcl8xMDBrIHwNCnw6LS0tLS0tLS0tLS0tLS0tLS0tOnw6LS0tLS0tLS0tOnw6LS0tLS0tLS0tLS06fDotLS06fDotLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS06fA0KfCAgICAgICBBbGdvbWEgICAgICAgfCAgMCB0byA0ICAgfCAgICAgMTgxICAgICB8IC4uLiB8ICAgICAgMzMwMiAgICAgIHwgICAgICAgIDAgICAgICAgIHwgICAgICAgICAgICAxNjQgICAgICAgICAgICB8DQp8ICAgICAgIEFsZ29tYSAgICAgICB8IDEyIHRvIDE5ICB8ICAgICA0MTIgICAgIHwgLi4uIHwgICAgICA0NDY0ICAgICAgfCAgICAgICAgMCAgICAgICAgfCAgICAgICAgICAgIDU0ICAgICAgICAgICAgIHwNCnwgICAgICAgIC4uLiAgICAgICAgIHwgICAgLi4uICAgIHwgICAgIC4uLiAgICAgfCAuLi4gfCAgICAgIC4uLiAgICAgICB8ICAgICAgIC4uLiAgICAgICB8ICAgICAgICAgICAgLi4uICAgICAgICAgICAgfA0KDQoqKipJbnRvIHNvbWV0aGluZyBsaWtlIHRoaXM6KioqDQoNCnwgcHVibGljX2hlYWx0aF91bml0IHwgcG9wdWxhdGlvbl8yMDIyIHwgY2F0ZWdvcnkgfCAwIHRvIDQgfCA1IHRvIDExIHwgMTIgdG8gMTkgfCAyMCB0byAzOSB8IDQwIHRvIDU5IHwgNjAgdG8gNzkgfCA4MCsgIHwNCnw6LS0tLS0tLS0tLS0tLS0tLS0tOnw6LS0tLS0tLS0tLS0tLS0tOnw6LS0tLS0tLS06fDotLS0tLS06fDotLS0tLS0tOnw6LS0tLS0tLS06fDotLS0tLS0tLTp8Oi0tLS0tLS0tOnw6LS0tLS0tLS06fDotLS0tOnwNCnwgICAgICAgQWxnb21hICAgICAgIHwgICAgIDExNzg0MCAgICAgIHwgIGNhc2VzICAgfCAgMzMwMiAgfCAgNDQ2NCAgIHwgICA3NTcwICAgfCAgIDQzMDEgICB8ICAgNDg5MCAgIHwgICAyMzMxICAgfCA0MTE2IHwNCnwgICAgICAgIC4uLiAgICAgICAgIHwgICAgICAgLi4uICAgICAgIHwgICAuLi4gICAgfCAgLi4uICAgfCAgIC4uLiAgIHwgICAuLi4gICAgfCAgIC4uLiAgICB8ICAgLi4uICAgIHwgICAuLi4gICAgfCAuLi4gIHwNCg0KV2UgbmVlZCB0byBkbyB0aGUgZm9sbG93aW5nIHRvIHRoZSBkYXRhc2V0DQoNCjEuICBTZWxlY3QganVzdCB0aGUgdmFyaWFibGVzIHdlIGFyZSBpbnRlcmVzdGVkIGluLg0KMi4gIHBpdm90IG91dCB0aGUgYWdlIGdyb3VwIGRhdGEgaW50byBpdHMgb3duIGNvbHVtbnMgc28gd2UgaGF2ZSAxIG9ic2VydmF0aW9uIChyb3cpIHBlciBQSFUuDQozLiAgQ29ycmVjdCB0aGUgcG9zaXRpb24gb2YgdGhlIGFnZSBncm91cHMgKDUgdG8gMTEgbmVlZHMgdG8gYmUgbW92ZWQpLg0KDQpgYGB7cn0NCiMgQ3JlYXRlIGEgd2lkZS1mb3JtYXQgdmVyc2lvbiBvZiBvdXIgbm9ybWFsaXplZCBkYXRhDQpjb3ZpZF9kZW1vZ3JhcGhpY3Nfbm9ybV93aWRlLmRmIDwtDQoNCiAgIyBTdGFydCBieSBwYXNzaW5nIGFsb25nIHRoZSBsb25nLWZvcm0gbm9ybWFsaXplZCBkYXRhDQogIGNvdmlkX2RlbW9ncmFwaGljc19ub3JtLmRmICU+JSANCg0KICAjIFNlbGVjdCBmb3IganVzdCB0aGUgUEhVLCBhZ2VfZ3JvdXAsIHBvcHVsYXRpb24gc2l6ZSBhbmQgY2FzZXMvaG9zcC9kZWF0aHNfcGVyXzEwMGsNCiAgZHBseXI6OnNlbGVjdChjKDMsNCwxMSwxMzoxNSkpICU+JSANCg0KICAjIFBpdm90IHRoZSBkYXRhIHRvIGEgbG9uZ2VyIGZvcm1hdA0KICBwaXZvdF9sb25nZXIoY29scyA9IC1jKDE6MyksIG5hbWVzX3RvID0gLi4uLCB2YWx1ZXNfdG8gPSAuLi4pICU+JSANCg0KICAjIEdldCByaWQgb2YgdGhlIHN1ZmZpeCBvZiAiX3Blcl8xMDBrIg0KICBtdXRhdGUoY2F0ZWdvcnkgPSAuLi4oc3RyaW5nID0gY2F0ZWdvcnksIHBhdHRlcm4gPSAiX3Blcl8xMDBrIikpICU+JSANCg0KICAjIFBpdm90IHRoZSBhZ2VfZ3JvdXAvcGVyXzEwMGsgZGF0YSBvdXQgd2lkZXINCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IC4uLiwNCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSAuLi4NCiAgICAgICAgICAgICApICU+JSANCg0KICAjIE1vdmUgdGhlICI1IHRvIDExIiBjYXRlZ29yeSB0byBhZnRlciAiMCB0byA0Ii4gWW91IGNvdWxkIHVzZSBhIGxvbmdlciAic2VsZWN0IiBjYWxsIHRvIGRvIHRoaXMgdG9vIQ0KICByZWxvY2F0ZSguLCBgNSB0byAxMWAsIC5hZnRlcj1gMCB0byA0YCkNCg0KIyBUYWtlIGEgbG9vayBhdCBvdXIgcmVzdWx0aW5nIGRhdGFmcmFtZQ0KaGVhZChjb3ZpZF9kZW1vZ3JhcGhpY3Nfbm9ybV93aWRlLmRmKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMS4yLjEgQ2FzdCBvdXIgZGF0YSB0byBhIG1hdHJpeCBmb3IgaGVhdG1hcHMNCg0KQXQgdGhpcyBwb2ludCwgd2Ugd291bGQgbm9ybWFsbHkgYmUgcHJlcGFyZWQgdG8gdmlzdWFsaXplIG91ciBkYXRhIHdpdGggYGdncGxvdGAgYnV0IG91ciBkYXRhIGlzIGluICoqd2lkZS1mb3JtYXQqKiBhbmQgd2UnbGwgYmUgdXNpbmcgYSBwYWNrYWdlIHRoYXQgcHJlZmVycyB0byB3b3JrIHdpdGggKiptYXRyaXgqKiBkYXRhLiBJbiB0aGF0IGNhc2UsIHdlIG5lZWQgdG8gc3RyaXAgdGhlIGRhdGEgZG93biBmdXJ0aGVyIGJlY2F1c2UgbWF0cml4IGRhdGEgbXVzdCBiZSBhbGwgb2YgdGhlIHNhbWUgdHlwZSBhbmQgd2Ugd2FudCB0byB3b3JrIHdpdGggbnVtZXJpYyBkYXRhISBXZSdsbCB1c2UgYGFzLm1hdHJpeCgpYCBmb3Igb3VyIGNvbnZlcnNpb24uDQoNCjEuICBXZSdsbCBmaWx0ZXIgb3VyIGRhdGFzZXQgdG8gb25seSBpbmNsdWRlIHRoZSAiKipjYXNlcyoqIiBkYXRhIHdlIHNhdyBhYm92ZSwgYW5kIHRoZW4gZHJvcCB0aGUgYGNhdGVnb3J5YCB2YXJpYWJsZSBmcm9tIGl0Lg0KMi4gIFdlJ2xsIG1vdmUgdGhlIGBwdWJsaWNfaGVhbHRoX3VuaXRgIGluZm9ybWF0aW9uIG92ZXIgdG8gdGhlIHJvdyBuYW1lcyBvZiBvdXIgbWF0cml4IHNvIHdlIGNhbiBzdGlsbCB0cmFjayBvdXIgZGF0YSBwcm9wZXJseS4NCjMuICBXZSdsbCBzdGlsbCBrZWVwIG91ciBgcG9wdWxhdGlvbl8yMDIyYCBjb2x1bW4gb2YgZGF0YSBidXQgd2UnbGwgaGF2ZSB0byByZW1lbWJlciB0aGF0IGl0J3MgdGhlcmUuDQoNCmBgYHtyfQ0KIyBOb3cgd2UgbmVlZCB0byBtYWtlIG91ciBtYXRyaXggYW5kIGFzc2lnbiByb3cgbmFtZXMuIA0KIyBJdCdzIGtpbmQgb2YgYXdrd2FyZCB0byBuZWVkIHRoaXMgcmVxdWlyZW1lbnQuDQoNCiMgQ2FzdCBvdXIgbWF0cml4IGFuZCBkcm9wIHRoZSBmaXJzdCBjb2x1bW4NCmNvdmlkX2RlbW9ncmFwaGljc19ub3JtLm14IDwtIGNvdmlkX2RlbW9ncmFwaGljc19ub3JtX3dpZGUuZGYgJT4lIA0KICANCiAgIyBGaWx0ZXIgZm9yIGp1c3QgY2FzZSBkYXRhDQogIGRwbHlyOjpmaWx0ZXIoLi4uKSAlPiUgDQogIA0KICAjIERyb3AgdGhlIFBIVSBhbmQgY2F0ZWdvcnkgZGF0YQ0KICBkcGx5cjo6c2VsZWN0KC1jKHB1YmxpY19oZWFsdGhfdW5pdCwgY2F0ZWdvcnkpKSAlPiUNCiAgDQogICMgQ29udmVydCB0byBhIG1hdHJpeA0KICBhcy5tYXRyaXgoKQ0KDQojIFNldCB0aGUgcm93IG5hbWVzIHVzaW5nIHRoZSBpbmZvcm1hdGlvbiBmcm9tIHRoZSBkYXRhIGZyYW1lDQpyb3duYW1lcyhjb3ZpZF9kZW1vZ3JhcGhpY3Nfbm9ybS5teCkgPC0gDQogIGNvdmlkX2RlbW9ncmFwaGljc19ub3JtX3dpZGUuZGYgJT4lIA0KICANCiAgIyBQdWxsIHRoZSBQSFUgbmFtZXMNCiAgLi4uICU+JSANCiAgDQogICMgQ3JlYXRlIGEgdW5pcXVlIHZlY3RvciBvZiB0aGVtDQogIC4uLg0KDQojIFRha2UgYSBwZWVrIGF0IG91ciBkYXRhDQpoZWFkKGNvdmlkX2RlbW9ncmFwaGljc19ub3JtLm14KQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLjMuMCBQbG90IGFuZCByZW9yZGVyIG91ciBQSFVzIGFzIGEgaGVhdG1hcCB3aXRoIGBIZWF0bWFwKClgDQoNCkZyb20gdGhlIGBDb21wbGV4SGVhdG1hcGAgcGFja2FnZSB3ZSBjYW4gdXNlIHRoZSBmdW5jdGlvbiBgSGVhdG1hcCgpYCB0byBnZW5lcmF0ZSBhIGhlYXRtYXAgdGhhdCBjYW4gZG8gYSBsaXR0bGUgbW9yZSB0aGFuIHdoYXQgd2Ugd2VyZSBkb2luZyB3aXRoIG91ciBgZ2dwbG90MmAgdmVyc2lvbi4gQSBuaWNlIGFzcGVjdCBvZiB1c2luZyBgSGVhdG1hcCgpYCBpcyB0aGF0IHdlIGNhbiBhc2sgaXQgdG8gcmVvcmRlciBvdXIgc2FtcGxlcyBhbG9uZyBib3RoIHRoZSByb3dzIGFuZCBjb2x1bW5zLiBBdCB0aGUgc2FtZSB0aW1lIHdlIGNhbiBwcmVzZW50IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBjb2x1bW5zIGVsZW1lbnRzIG9yIHJvdyBlbGVtZW50cyBpbiB0aGUgZm9ybSBvZiBkZW5kcm9ncmFtcy4NCg0KIyMjIDEuMy4xIEhvdyBkbyB3ZSByZW9yZGVyIG91ciBkYXRhIHZpYSBjbHVzdGVyaW5nPw0KDQpJbiBpdHMgY3VycmVudCBmb3JtLCB3ZSBoYXZlIG91ciBvcmRlcmVkIG91ciBkYXRhIGFsb25nIHRoZSBjb2x1bW5zIGluIGFuIGFzY2VuZGluZyBvcmRpbmFsIGFycmFuZ2VtZW50LiBXaGlsZSB0aGlzIG1ha2VzIHNlbnNlIHRvIHVzIG5vdywgaXQgbWF5IG5vdCBoZWxwIHRvIHZpc3VhbGx5IGlkZW50aWZ5IHRoZSBzdHJvbmdlc3QgdHJlbmRzIG9yIGdyb3VwaW5ncyBpbiBvdXIgZGF0YS4gQ2x1c3RlcmluZyBhdHRlbXB0cyB0byBicmluZyBvcmRlciB0byBvdXIgZGF0YSBieSBncm91cGluZyBkYXRhIGFjY29yZGluZyB0byBzcGVjaWZpYyBhbGdvcml0aG1zLg0KDQpPdmVyYWxsIHNpbmdsZSBwb2ludHMgYXJlIHVzdWFsbHkgZ3JvdXBlZCB0b2dldGhlciBhcyBuZWlnaGJvdXJzIHdpdGggdGhlICJuZWFyZXN0IiBuZWlnaGJvdXJzIGRldGVybWluZWQgYnkgYSBtZXRyaWMgb2Ygc29tZSBraW5kLiBUaGVzZSBjbHVzdGVycyBhcmUgdGhlbiBmdXJ0aGVyIGdyb3VwZWQgdXNpbmcgdGhlIHNhbWUgbWV0cmljcyB1bnRpbCB0aGUgZW50aXJlIHNldCBpcyBwcmVzZW50ZWQgaW4gdGhlc2Ugb3JkZXJlZCBncm91cHMuIFRoZXNlIGdyb3VwaW5ncy9yZWxhdGlvbnNoaXBzIGNhbiBhbHNvIGJlIHByZXNlbnRlZCBhcyBkZW5kcm9ncmFtcy4gSW4gb3VyIGN1cnJlbnQgY2FzZSwgd2UgYXJlbid0IGNvbmNlcm5lZCB3aXRoIGRldGVybWluaW5nIGdyb3VwcyAqcGVyIHNlKiBidXQgcmF0aGVyIHdpdGgganVzdCBjb25uZWN0aW5nIHRoZW0gYnkgdGhlaXIgc2ltaWxhcml0eSBhbmQgdGhlbiBjcmVhdGluZyBhIGhpZXJhcmNoaWNhbCBvcmRlci4NCg0KT3VyIGRhdGEgaXMgdXN1YWxseSBjb2RlZCBpbiBuLWRpbWVuc2lvbmFsIHNwYWNlLCBkZXBlbmRpbmcgb24gdGhlIG5hdHVyZSBvZiBvdXIgZGF0YXNldC4gRm9yIGBjb3ZpZF9kZW1vZ3JhcGhpY3Nfbm9ybS5teGAgb3VyIDcgY29sdW1ucyBhcmUgY29kZWQgYnkgZGF0YSBmcm9tIDM0IFBIVXMgbWVhbmluZyBlYWNoIGNvbHVtbiBpcyBjb2RlZCBpbiAzNCBkaW1lbnNpb25zIG9yIDM0IGZlYXR1cmVzLiBDb252ZXJzZWx5LCBvdXIgMzQgcm93cyByZXByZXNlbnQgUEhVcyBlYWNoIGNvZGVkIGJ5IGRhdGEgYWNyb3NzIDcgYWdlIGdyb3VwcyBhbmQgdGhlcmVmb3JlIDcgZGltZW5zaW9ucy4NCg0KSW1wb3J0YW50IG9yIGhlbHBmdWwgcGFyYW1ldGVycyB0byBydW4gYEhlYXRtYXAoKWA6DQoNCi0gICBgbWF0cml4YDogb3VyIG1hdHJpeCBvYmplY3QuIEl0ICoqKm11c3QgYmUgYSBtYXRyaXgqKiosIGFuZCBub3QgYSBkYXRhIGZyYW1lLiBJdCBjb3VsZCBhbHNvIGJlIGEgdmVjdG9yIGJ1dCB0aGF0IGJlY29tZXMgYSBzaW5nbGUgY29sdW1uLg0KLSAgIGBjb2xgOiBkZXRlcm1pbmVzIHRoZSBjb2xvdXJzIHVzZWQgZm9yIHRoZSBpbWFnZS4NCi0gICBgbmFtZWA6IHRoZSBuYW1lL3RpdGxlIG9mIHRoZSBoZWF0bWFwIChhbHNvIHVzZSBhcyB0aGUgbGVnZW5kIHRpdGxlIGJ5IGRlZmF1bHQpDQotICAgYHJvd18qYCA6IHNldCBvdXIgcm93IHRleHQgcHJvcGVydGllcyBpbmNsdWRpbmcgdGl0bGVzIGFuZCBsYWJlbHM6DQogICAgLSAgIGByb3dfdGl0bGVgLCBgcm93X3RpdGxlX3NpZGVgLCBgcm93X3RpdGxlX2dwYCAoZ3JhcGhpYyBwcm9wZXJ0aWVzKQ0KICAgIC0gICBgcm93X25hbWVzX3NpZGVgIGByb3dfbmFtZXNfZ3BgDQogICAgLSAgIGNvbHVtbiBwcm9wZXJ0aWVzIGNhbiBhbHNvIGJlIGNoYW5nZWQgdXNpbmcgYGNvbHVtbl8qYA0KLSAgIGBjbHVzdGVyX3Jvd3NgIGFuZCBgY2x1c3Rlcl9jb2x1bW5zYDogbG9naWNhbCBwYXJhbWV0ZXJzIHRvIGRldGVybWluZSBpZiBjbHVzdGVyaW5nIHNob3VsZCBvY2N1ciAoZGVmYXVsdCBpcyBgVFJVRWApDQotICAgYHNob3dfaGVhdG1hcF9sZWdlbmRgOiB3aGV0aGVyIG9yIG5vdCB0byBzaG93IHRoZSBoZWF0bWFwIGxlZ2VuZA0KLSAgIGBoZWF0bWFwX2xlZ2VuZF9wYXJhbWA6IFNldCB0aGUgdGl0bGUgb2YgdGhlIGxlZ2VuZCBzcGVjaWZpY2FsbHkgaXMgYGxpc3QodGl0bGUgPSAieCIpYA0KLSAgIGBzaG93X1tyb3cvY29sXV9kZW5kYDogV2hldGhlciBvciBub3QgdG8gc2hvdyBkZW5kb2dyYW1zIGZvciB0aGUgcm93L2NvbHVtbg0KDQpUaGVyZSBhcmUgYWN0dWFsbHkgKmEgbG90KiBvZiBvcHRpb25zIGJ1dCB0aGVzZSBzaG91bGQgaGVscCB1cyBtYWtlIGEgYmFzaWMgb25lLiBMZXQncyBwbG90IG91ciBkYXRhIHdpdGggYW5kIHdpdGhvdXQgYSBkZW5kcm9ncmFtIHRvIGNvbXBhcmUuDQoNCmBgYHtyfQ0KP0hlYXRtYXANCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEwfQ0KDQojIENyZWF0ZSBhIEhlYXRtYXAgb2JqZWN0DQoNCmNhc2VzX2htYXAgPC0gDQogICMgU3VwcGx5IG91ciBtYXRyaXggbWludXMgdGhlIHBvcHVsYXRpb25zIHNpemUNCiAgSGVhdG1hcCguLi4sICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSAuLi4sIGNsdXN0ZXJfY29sdW1ucyA9IC4uLiwgIyBEb24ndCBjbHVzdGVyIG9uIGVpdGhlciByb3dzIG9yIGNvbHVtbnMNCg0KICAgICAgICAgICMgVXNlIGNvbHVtbl90aXRsZSBhcyB0aGUgdGl0bGUgb2Ygb3VyIGhlYXRtYXANCiAgICAgICAgICBjb2x1bW5fdGl0bGUgPSAiSGVhdG1hcCBvZiBDT1ZJRC0xOSBjYXNlcyBpbiBQSFVzIGJ5IGFnZSBncm91cDogdW5jbHVzdGVyZWQiLA0KICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICMgUm90YXRlIHRoZSBsZWdlbmQgaG9yaXpvbnRhbGx5IGFuZCBnaXZlIGl0IGEgdGl0bGUNCiAgICAgICAgICBoZWF0bWFwX2xlZ2VuZF9wYXJhbSA9IGxpc3QodGl0bGUgPSAiY2FzZXMgcGVyIDEwMEsgaW5kaXZpZHVhbHMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmRfZGlyZWN0aW9uID0gImhvcml6b250YWwiKSwNCiAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAjIFJvdGF0ZSBjb2x1bW4gbmFtZXMgdG8gaG9yaXpvbnRhbA0KICAgICAgICAgIGNvbHVtbl9uYW1lc19yb3QgPSAwLA0KICAgICAgICAgIGNvbHVtbl9uYW1lc19jZW50ZXIgPSBUUlVFDQogICAgICAgICApDQoNCiMgUGxvdCB0aGUgaGVhdG1hcCANCi4uLihjYXNlc19obWFwLCANCiAgICAgIyBQbG90IHRoZSBsZWdlbmQgb24gdGhlIGJvdHRvbQ0KICAgICBoZWF0bWFwX2xlZ2VuZF9zaWRlID0gImJvdHRvbSINCiAgICApDQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0NCg0KIyBDcmVhdGUgYSBIZWF0bWFwIG9iamVjdA0KY2FzZXNfaG1hcCA8LSANCiAgIyBTdXBwbHkgb3VyIG1hdHJpeCBtaW51cyB0aGUgcG9wdWxhdGlvbnMgc2l6ZQ0KICBIZWF0bWFwKGNvdmlkX2RlbW9ncmFwaGljc19ub3JtLm14WywtMV0sICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgY2x1c3Rlcl9yb3dzID0gLi4uLCBjbHVzdGVyX2NvbHVtbnMgPSAuLi4sICAgIyBDbHVzdGVyIG9uIGJvdGggcm93cyBhbmQgY29sdW1ucw0KICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICMgVXNlIGNvbHVtbl90aXRsZSBhcyB0aGUgdGl0bGUgb2Ygb3VyIGhlYXRtYXANCiAgICAgICAgICBjb2x1bW5fdGl0bGUgPSAiSGVhdG1hcCBvZiBDT1ZJRC0xOSBjYXNlcyBpbiBQSFVzIGJ5IGFnZSBncm91cDogY2x1c3RlcmVkIiwNCiAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAjIFJvdGF0ZSB0aGUgbGVnZW5kIGhvcml6b250YWxseSBhbmQgZ2l2ZSBpdCBhIHRpdGxlDQogICAgICAgICAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gImNhc2VzIHBlciAxMDBLIGluZGl2aWR1YWxzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kX2RpcmVjdGlvbiA9ICJob3Jpem9udGFsIiksDQogICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgIyBSb3RhdGUgY29sdW1uIG5hbWVzIHRvIGhvcml6b250YWwNCiAgICAgICAgICBjb2x1bW5fbmFtZXNfcm90ID0gMCwNCiAgICAgICAgICBjb2x1bW5fbmFtZXNfY2VudGVyID0gVFJVRQ0KICAgICAgICAgKQ0KDQojIFBsb3QgdGhlIGhlYXRtYXAgDQpkcmF3KGNhc2VzX2htYXAsIA0KICAgICAjIFBsb3QgdGhlIGxlZ2VuZCBvbiB0aGUgYm90dG9tDQogICAgIGhlYXRtYXBfbGVnZW5kX3NpZGUgPSAiYm90dG9tIg0KICAgICkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDEuMy4yIENvbmNhdGVuYXRlIG11bHRpcGxlIGhlYXRtYXBzIHRvZ2V0aGVyIGludG8gYSBgSGVhdG1hcExpc3RgIG9iamVjdA0KDQpPdXIgaGVhdG1hcCBhYm92ZSBpcyBkcmF3biBmcm9tIGEgc2luZ2xlIGRhdGFzZXQgY2F0ZWdvcnkgLSAqKmNhc2VzKiogLSBidXQgaXQgaGVscHMgdXMgdG8gc2VlIHRoYXQgdGhlcmUgYXJlIHNvbWUgc3Ryb25nIHNpZ25hbHMgdGhhdCBkaWZmZXJlbnRpYXRlIGJldHdlZW4gb3VyIGRpZmZlcmVudCBQSFVzLiBOb3cgdGhpcyBpcyBhIDM0eDcgZ3JpZCB3aGVyZSB3ZSBjYW4gaW52ZXN0aWdhdGUgYWxsIG9mIHRoZSBkYXRhIGluIG91ciBkYXRhc2V0LiBXaGF0IGhhcHBlbnMgd2hlbiB3ZSB3YW50IHRvIHByb2R1Y2UgdGhpcyBraW5kIG9mIGRhdGEgZnJvbSBvdXIgb3RoZXIgdHdvIG1ldHJpY3Mgb2YgaG9zcGl0YWxpemF0aW9ucyBhbmQgZGVhdGhzPw0KDQpUbyBhY2NvbXBsaXNoIHRoaXMsIHdlIGNhbiByZXBlYXQgb3VyIHN0ZXBzIGFuZCBjcmVhdGUgMyBzZXBhcmF0ZSBgSGVhdG1hcGAgb2JqZWN0cyAqKipidXQqKiogd2UgY2FuIHBsb3QgdGhlbSB0b2dldGhlciBhcyBhIHNpbmdsZSAqY29tcGxleCBoZWF0bWFwKi4gSW4gZmFjdCwgcmF0aGVyIHRoYW4gc3RvcmUgbXVsdGlwbGUgaGVhdG1hcCBvYmplY3RzLCB3ZSdsbCBjcmVhdGUgYSBgSGVhdG1hcExpc3RgIG9iamVjdCBieSBjb25jYXRlbmF0aW5nIHRvZ2V0aGVyIG11bHRpcGxlIGBIZWF0bWFwYCBvYmplY3RzIHVzaW5nIGEgYGZvcmAgbG9vcC4NCg0KV2UnbGwgcmVjcmVhdGUgb3VyIGNvZGUgZnJvbSBhYm92ZSBidXQgc2ltcGxpZnkgdGhlIGhlYXRtYXAgY29kZSBhIGxpdHRsZS4gV2hpbGUgd2UncmUgYXQgaXQsIHdlJ2xsIGFsc28gY29udmVydCBvdXIgY29sb3Vyc2NoZW1lIHRvIHZpcmlkaXMgd2hpbGUgd2UncmUgYXQgaXQNCg0KYGBge3J9DQojIENyZWF0ZSBhIGxpc3Qgb2YgaGVhdG1hcCBvYmplY3RzIGZyb20gb3VyIGRlbW9ncmFwaGljIGRhdGENCmhtX2xpc3QgPC0gTlVMTA0KDQojIENyZWF0ZSBzb21lIHF1aWNrIHZlY3RvcnMgdG8gaGVscCBvdXJzZWx2ZXMgb3V0DQpjYXRlZ29yaWVzIDwtIC4uLg0KDQojIFN0b3JlIHRoZSByb3duYW1lcyB3ZSB3YW50IHRvIHVzZSBvbiBvdXIgbWF0cmljZXMNCm14X3Jvd25hbWVzIDwtIGNvdmlkX2RlbW9ncmFwaGljc19ub3JtX3dpZGUuZGYgJT4lIHB1bGwocHVibGljX2hlYWx0aF91bml0KSAlPiUgdW5pcXVlKCkNCg0KIyBVc2UgYSBsb29wIHRvIHNlcGFyYXRlIHRoZSBkYXRhIGJ5IGNhdGVnb3J5DQpmb3IgKGkgaW4gY2F0ZWdvcmllcykgew0KICANCiAgIyBDcmVhdGUgYSB0ZW1wb3JhcnkgbWF0cml4IG9mIHRoZSBzcGVjaWZpYyBjYXRlZ29yeSBkYXRhDQogIHRlbXBfbXggPC0gY292aWRfZGVtb2dyYXBoaWNzX25vcm1fd2lkZS5kZiAlPiUgDQogICAgICBkcGx5cjo6ZmlsdGVyKGNhdGVnb3J5ID09IGkpICU+JSANCiAgICAgIGRwbHlyOjpzZWxlY3QoLWMocHVibGljX2hlYWx0aF91bml0LCBjYXRlZ29yeSkpICU+JSANCiAgICAgIGFzLm1hdHJpeCgpDQogIA0KICAjIFNldCB0aGUgcm93bmFtZXMNCiAgcm93bmFtZXModGVtcF9teCkgPC0gbXhfcm93bmFtZXMNCiAgDQogICMgQ3JlYXRlIGEgSGVhdG1hcCBvYmplY3QNCiAgaG1hcCA8LSBIZWF0bWFwKHRlbXBfbXhbLC0xXSwgICAjIFN1cHBseSBvdXIgbWF0cml4IG1pbnVzIHRoZSBwb3B1bGF0aW9ucyBzaXplDQogICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICMgVXNlIGEgdmlyaWRpcyBjb2xvdXJzY2FsZSBicm9rZW4gaW50byAxMDAgc2VnbWVudHMNCiAgICAgICAgICAgICAgICAgIGNvbCA9IHZpcmlkaXMoMTAwKSwNCg0KICAgICAgICAgICAgICAgICAgIyBVc2UgY29sdW1uX3RpdGxlIGFzIHRoZSB0aXRsZSBvZiBvdXIgaGVhdG1hcA0KICAgICAgICAgICAgICAgICAgY29sdW1uX3RpdGxlID0gaSwNCiAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgIyBSb3RhdGUgdGhlIGxlZ2VuZCBob3Jpem9udGFsbHkgYW5kIGdpdmUgaXQgYSB0aXRsZSBiYXNlZCBvbiBjYXRlZ29yeQ0KICAgICAgICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gcGFzdGUwKGksICIgcGVyIDEwMEsgaW5kaXZpZHVhbHMiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmRfZGlyZWN0aW9uID0gImhvcml6b250YWwiKQ0KICAgICAgICAgICAgICAgICApDQogIA0KICAjIEFkZCB0aGlzIHRvIG91ciBoZWF0bWFwIGxpc3QNCiAgaG1fbGlzdCA8LSAuLi4NCn0NCg0KIyBXaGF0IGtpbmQgb2Ygb2JqZWN0IGlzIGhtX2xpc3Q/DQpjbGFzcyhobV9saXN0KQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMS4zLjMgYGRyYXcoKWAgeW91ciBgSGVhdG1hcExpc3RgDQoNCk5vdyB0aGF0IHdlIGhhdmUgb3VyIGBIZWF0bWFwTGlzdGAgd2UgY2FuIHNpbXBseSBkcmF3IHRoZSB3aG9sZSB0aGluZyBhbmQgaXQgd2lsbCBhdXRvbWF0aWNhbGx5IGNvbmNhdGVuYXRlIGZvciB1cy4gT2YgY291cnNlIHRoZXJlIGFyZSBtYW55IG9wdGlvbnMgd2UgY2FuIHVzZSB0byBkaXNwbGF5IHRoZSBsaXN0LiBPbmUgdGhpbmcgdG8gbm90ZSBpcyB0aGF0IHRoZSBbKmNsdXN0ZXJpbmcgb2YgY29sdW1ucyBpbiBlYWNoIGhlYXRtYXAgaXMgaW5kZXBlbmRlbnQqXXsudW5kZXJsaW5lfSB3aGlsZSB0aGUgWypjbHVzdGVyaW5nIG9mIHJvd3MgKGFuZCB0aHVzIG9yZGVyKSBpcyBiYXNlZCBvbiBvbmx5IHRoZSBmaXJzdCBoZWF0bWFwKl17LnVuZGVybGluZX0gKGJ5IGRlZmF1bHQpLg0KDQpVc2luZyB0aGUgYGRyYXcoKWAgbWV0aG9kIHRvIGRpc3BsYXkgeW91ciBgSGVhdG1hcExpc3RgIHdpbGwgYWxsb3cgeW91IHRvIGZ1cnRoZXIgY3VzdG9taXplIGRldGFpbHMgb2YgdGhlIG92ZXJhbGwgZmlndXJlIGluY2x1ZGluZyBzZXR0aW5nIGByb3dfdGl0bGVgIGFuZCBgY29sdW1uX3RpdGxlYCBmb3IgdGhlIGVudGlyZSBmaWd1cmUuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTgsIGZpZy5oZWlnaHQ9MTB9DQoNCmRyYXcoLi4uLCANCiAgICAgY29sdW1uX3RpdGxlID0gIkNPVklELTE5IG1ldHJpY3MgYnJlYWtkb3duIGJ5IGFnZSBncm91cCBhbmQgUEhVXG4iLA0KICAgICBjb2x1bW5fdGl0bGVfZ3AgPSBncGFyKGZvbnRzaXplID0gMjApDQogICAgKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLjQuMCBCdWlsZGluZyBjb3JyZWxhdGlvbiBoZWF0bWFwcyB3aXRoIGNvbXBsZXggUk5BLVNlcSBkYXRhDQoNCk91ciBoZWF0bWFwIGFib3ZlIGlzIGRyYXduIGZyb20gYSByZWxhdGl2ZWx5IHNpbXBsZSBkYXRhc2V0IGJ1dCBpdCBoZWxwcyB1cyB0byBzZWUgdGhhdCB0aGVyZSBhcmUgc29tZSBzdHJvbmcgc2lnbmFscyB0aGF0IGRpZmZlcmVudGlhdGUgYmV0d2VlbiBvdXIgZGlmZmVyZW50IFBIVXMuIEl0IGJlY29tZSBjbGVhciB0aGF0IHdoaWxlIHRoZSBidWxrIG9mIGNhc2VzIGluIGVhY2ggUEhVIGlzIGRvbWluYXRlZCBieSB0aGUgMjAtMzkgYWdlIHNlZ21lbnQsIHRoZSBidWxrIG9mIGhvcHNpdGFsaXphdGlvbnMgYW5kIGRlYXRocyBvcmlnaW5hdGUgZnJvbSB0aGUgODArIHNlZ21lbnQuDQoNCk9mIGNvdXJzZSwgdGhpcyBkYXRhIHdhcyBjdW11bGF0aXZlIGluZm9ybWF0aW9uIGZyb20gSmFudWFyeSAyMDIwIHRvIE1hcmNoIDIwMjIuIFdpdGggdGhlIGFmdGVybWF0aCBvZiB2YWNjaW5hdGlvbnMgYW5kIGRpZmZlcmVudCB2YXJpYW50cywgYSBsb29rIGF0IHRoZSBjdXJyZW50IGRlbW9ncmFwaGljcyBtYXkgcmV2ZWFscyBhIHNoaWZ0IGluIHRoZXNlIHRyZW5kcy4NCg0KQ29uc2lkZXJpbmcgdGhhdCB0aGUgaGVhdG1hcCBpcyBhIDM0eDcgZ3JpZCwgaXQgaXMgZWFzaWVyIHRvIHZpc3VhbGl6ZSBhbmQgZGlzc2VjdCBhbGwgb2YgdGhlIGRhdGEgaW4gb3VyIGRhdGFzZXQuIFdoYXQgaGFwcGVucywgaG93ZXZlciwgd2hlbiB3ZSB3YW50IHRvIHByb2R1Y2UgdGhpcyBraW5kIG9mIHZpc3VhbGl6YXRpb24gZnJvbSBzb21ldGhpbmcgbXVjaCBsYXJnZXIgb3IgbW9yZSBjb21wbGV4Pw0KDQpSZWNhbGwgZnJvbSBsYXN0IGxlY3R1cmUgdGhhdCB3ZSBpbnZlc3RpZ2F0ZWQgcmVhZCBjb3VudCBkYXRhIGZyb20gV3lsZXIgZXQgYWwuLCAyMDIwIC0gYFd5bGVyMjAyMF9BRUNfU0FSU0NvVjJfMTdBQUdfcmVhZGNvdW50cy50c3ZgLiBXZSB1c2VkIHRoaXMgZGF0YXNldCB0byBwcm9kdWNlIGEgc2NhdHRlcnBsb3QgbWF0cml4IHRvIGNvbXBhcmUgYmV0d2VlbiBzb21lIG9mIHRoZSByZXBsaWNhdGUgZGF0YSB3aXRoaW4gdGhlIHNldC4gQWNyb3NzIHRoaXMgc2V0IHRoZXJlIGFyZSAzNiBzZXRzIG9mIFJOQS1TZXEgZGF0YSBzcGFubmluZyAyNywwMTEgZ2VuZXMuDQoNCkxldCdzIGJlZ2luIGJ5IG9wZW5pbmcgdXAgdGhlIGRhdGEgZmlsZSBhbmQgZ2V0dGluZyBhIHF1aWNrIHJlbWluZGVyIG9mIHdoYXQgaXQgbG9va3MgbGlrZS4NCg0KYGBge3J9DQojIFJlYWQgaW4geW91ciByZWFkX2NvdW50IGRhdGENCnd5bGVyX3JlYWRjb3VudHMuZGYgPC0gcmVhZF90c3YoLi4uKQ0KDQpzdHIod3lsZXJfcmVhZGNvdW50cy5kZiwgZ2l2ZS5hdHRyID0gRkFMU0UpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjQuMSBXcmFuZ2xlIG91ciByZWFkY291bnQgZGF0YQ0KDQpBcyB5b3UgbWlnaHQgcmVjYWxsIGZyb20gbGFzdCB3ZWVrLCB0aGVyZSBpcyBxdWl0ZSBhIGJpdCBvZiBkYXRhIGluIHRoaXMgc2V0LiBTbyB0aGF0IHdlIGNhbiBtb3JlIGVhc2lseSB2aXN1YWxpemUgb3VyIGRhdGEsIHdlJ2xsIHdhbnQgdG8gd3JhbmdsZSBpdCBhIGJpdCBtb3JlIGJ5Og0KDQoxLiAgdXBkYXRpbmcgdGhlIGNvbHVtbiBuYW1lcyB0byByZW1vdmUgc29tZSBvZiB0aGUgdW5uZWNlc3NhcnkgdGl0bGUgaW5mb3JtYXRpb24uDQoyLiAgZmlsdGVyaW5nIGJ5IHJlYWRjb3VudHMgdG8gbGltaXQgb3VyIGdlbmVzIHRvIHRob3NlIHdpdGggdmFsdWVzIGJldHdlZW4gMTAwMCBhbmQgMzAwMC4NCjMuICBDb252ZXJ0IG91ciBkYXRhZnJhbWUgdG8gYSBtYXRyaXggYW5kIHJlbmFtZSB0aGUgcm93cyB1c2luZyB0aGUgZ2VuZXMuDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtaW5mb30NCioqV2h5IGRvIHdlIGZpbHRlciBvdXIgcmVhZCBjb3VudHM/KiogSW4gdGhpcyBpbnN0YW5jZSB3ZSBoYXZlIGNob3NlbiB0byBmaWx0ZXIgb3VyIHJlYWQgY291bnRzLiBJbiBzb21lIGNhc2VzLCB5b3UgKm1pZ2h0KiBmaW5kIHdpbGQgc3dpbmdzIGluIGV4cHJlc3Npb24sIGFuZCBpdCBpcyB3aGF0IHdlJ3JlIGxvb2tpbmcgZm9yIG1vc3Qgb2YgdGhlIHRpbWUuIEZvciBpbi1jbGFzcyBhbmFseXNpcywgdG8gc2F2ZSBhIGJpdCBvbiBtZW1vcnkgYW5kIHRpbWUsIHdlIHRyeSB0byBsaW1pdCB0aGUgcmVhZCBjb3VudHMgdG8gYSBuYXJyb3cgc2V0IHRvIHJlZHVjZSBvdXIgc2V0IHNpemUuIEl0IHdpbGwgYWxzbyBncmVhdGx5IHJlZHVjZSB0aGUgc2l6ZSBvZiBvdXIgaGVhdG1hcCBmb3Igdmlld2luZy4gQnkgZmlsdGVyaW5nLCB3ZSdsbCBnbyBmcm9tIDI3LDAxMSBvYnNlcnZhdGlvbnMgdG8gMTA5Lg0KOjo6DQoNCmBgYHtyfQ0Kd3lsZXJfcmVhZGNvdW50c19maWx0ZXJlZC5kZiA8LQ0KICAgIA0KICAgIHd5bGVyX3JlYWRjb3VudHMuZGYgJT4lIA0KDQogICAgIyBSZW5hbWUgdGhlIGNvbHVtbnMgYmUgcmVtb3ZpbmcgdGhlIGZpcnN0IHBvcnRpb246IEFFQ0lJX3h4DQogICAgcmVuYW1lX3dpdGgoLiwgfiBzdHJfcmVwbGFjZShzdHJpbmcgPSAueCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gciIoXHcqX1xkKl8pIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlID0gIiIpKSAlPiUgDQoNCiAgICAjIGZpbHRlciBvdXQgdGhlIGxvdy1yZWFkY291bnQgZGF0YQ0KICAgIGZpbHRlcihpZl9hbGwoLmNvbHMgPSAtMSwgLmZucyA9IH4gLnggPiAuLi4gJiAueCA8IC4uLikpDQoNCiMgQ3JlYXRlIG91ciBkYXRhIG1hdHJpeA0Kd3lsZXJfcmVhZGNvdW50cy5teCA8LSBhcy5tYXRyaXgod3lsZXJfcmVhZGNvdW50c19maWx0ZXJlZC5kZlssIC4uLl0pDQoNCiMgU2V0IHRoZSByb3cgbmFtZXMgdXNpbmcgdGhlIGluZm9ybWF0aW9uIGZyb20gdGhlIGRhdGEgZnJhbWUNCnJvd25hbWVzKHd5bGVyX3JlYWRjb3VudHMubXgpIDwtIHd5bGVyX3JlYWRjb3VudHNfZmlsdGVyZWQuZGYkZ2VuZQ0KDQojIENoZWNrIHRoZSBjaGFyYWN0ZXJpc3RpY3Mgb2Ygb3VyIG1hdHJpeA0KaGVhZCh3eWxlcl9yZWFkY291bnRzLm14KQ0Kc3RyKHd5bGVyX3JlYWRjb3VudHMubXgpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjQuMiBQbG90IGEgaGVhdG1hcCBvZiB5b3VyIHJlYWRjb3VudCBkYXRhDQoNCldoYXQgaWYgd2Ugd2VyZSB0byBwcm9kdWNlIGEgaGVhdG1hcCBkaXJlY3RseSB3aXRoIHRoZSByYXcgZGF0YT8gSXQgd291bGQgYmUgaW1wb3NzaWJsZSB0byByZWFkLCBhbmQgaW5jbHVkaW5nIGEgZGVuZHJvZ3JhbSB3b3VsZCBhY3R1YWxseSB0YWtlIGZvcmV2ZXIgdG8gcHJvY2VzcyBnaXZlbiB0aGUgMjcsMDExIHJvd3Mgb2YgZGF0YSB0byBwcm9jZXNzIGFjcm9zcyB0aGUgMzYgZGF0YXNldHMuIFRoYXQncyB3aHkgd2UnbGwgdXNlIG91ciBzbWFsbCBzdWJzZXQgb2YgZGF0YSBhcyBhbiBleGFtcGxlIHRvIGJ1aWxkIGEgaGVhdG1hcC4NCg0KTGV0J3MgcGxvdCB0aGUgZGF0YSBub3cgd2l0aCBgaGVhdG1hcC4yYCBhbmQgc2VlIGhvdyBpdCBsb29rcyB3aXRoIGEgZGVuZHJvZ3JhbS4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0yMH0NCg0KIyBDcmVhdGUgYSBIZWF0bWFwIG9iamVjdA0Kd3lsZXJfaG1hcCA8LSBIZWF0bWFwKC4uLiwgICAgICAgICAgICAgICAjIFN1cHBseSBvdXIgbWF0cml4IA0KICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsIGNsdXN0ZXJfY29sdW1ucyA9IFRSVUUsICAgIyBDbHVzdGVyIG9uIGJvdGggcm93cyBhbmQgY29sdW1ucw0KICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IC4uLiwNCiAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAjIFVzZSBjb2x1bW5fdGl0bGUgYXMgdGhlIHRpdGxlIG9mIG91ciBoZWF0bWFwDQogICAgICAgICAgICAgICAgICAgICAgY29sdW1uX3RpdGxlID0gIkhlYXRtYXAgb2YgV3lsZXIgZXQgYWwuLCAyMDIwIHJlYWRjb3VudCBkYXRhIiwNCiAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAjIFJvdGF0ZSB0aGUgbGVnZW5kIGhvcml6b250YWxseSBhbmQgZ2l2ZSBpdCBhIHRpdGxlDQogICAgICAgICAgICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gInJlYWRjb3VudHMgcGVyIGdlbmUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmRfZGlyZWN0aW9uID0gImhvcml6b250YWwiKSwNCiAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICkNCg0KIyBQbG90IHRoZSBoZWF0bWFwIA0KZHJhdyh3eWxlcl9obWFwLCANCiAgICAgIyBQbG90IHRoZSBsZWdlbmQgb24gdGhlIGJvdHRvbQ0KICAgICBoZWF0bWFwX2xlZ2VuZF9zaWRlID0gImJvdHRvbSINCiAgICApDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjQuMyBHZW5lcmF0ZSBhIGNvcnJlbGF0aW9uIG1hdHJpeCBvZiB5b3VyIGRhdGENCg0KRnJvbSB0aGUgYWJvdmUgb3V0cHV0LCB3ZSBjYW4gc2VlIHRoYXQgb3VyIGhlYXRtYXAgaXMgYWxyZWFkeSBxdWl0ZSBoYXJkIHRvIHJlYWQgYW5kIHRoYXQncyBjb21pbmcgb2ZmIG9mIHVzaW5nIGp1c3QgMTA5IGdlbmVzISBXZSBkbywgaG93ZXZlciwgZ2V0IHNvbWUgc3Ryb25nIHRyZW5kcyByZWdhcmRpbmcgb3VyIGRhdGFzZXRzIC0gd2UgY2FuIHNlZSBjZXJ0YWluIHNldHMgb2YgcmVwbGljYXRlcyBhcmUgZ3JvdXBlZCB0b2dldGhlciBpbiB0aGUgZGF0YSBidXQgbm90IGFsbCBvZiB0aGVtLiBUaGlzIG1pZ2h0IGJlIGNsZWFyZXIgaWYgd2Ugd2VyZSB0byBmYWN0b3IgaW4gYWxsIG9mIHRoZSBkYXRhcG9pbnRzIG9yIGp1c3QgdGhlIHJlbGV2YW50IGdlbmVzIHRoYXQgZGVmaW5lIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGRhdGFzZXRzLiBXZSdsbCBkaXNjdXNzIHRoZSBzZWNvbmQgcGFydCBvZiB0aGF0IHRob3VnaHQgbGF0ZXIgaW4gdGhpcyBsZWN0dXJlLg0KDQpSZWNhbGwgdGhhdCB3aGF0IHdlJ3JlIHJlYWxseSBpbnRlcmVzdGVkIGluLCByZWdhcmRpbmcgdGhlc2UgcmVhZGNvdW50IGRhdGEsIGlzIHRvIGFzayBqdXN0IGhvdyBzaW1pbGFyIHRoZSBleHBlcmltZW50cyBhcmUgYmV0d2VlbiBlYWNoIG90aGVyLiBXaGVyZWFzIHdlIHdlcmUgYSBsaXR0bGUgbGltaXRlZCBieSB0aGUgc3BhY2UgbmVlZGVkIHRvIHByb2R1Y2UgdGhlIHNjYXR0ZXJwbG90IG1hdHJpeCwgd2Ugd2VyZSBhYmxlIHRvIHByb2R1Y2UgY29ycmVsYXRpb24gdmFsdWVzIGJldHdlZW4gZXhwZXJpbWVudHMgb24gYSBzbWFsbCBzY2FsZS4gV2UgY2FuIGV4dGVuZCB0aGF0IHZpc3VhbGl6YXRpb24gZm9yd2FyZCB0byBjb21wYXJlIGJldHdlZW4gKmFsbCogZGF0YXNldHMgYW5kIHRoZW4gdmlzdWFsbHkgc3VtbWFyaXplIHRoZSBkYXRhIGFzIGEgaGVhdG1hcC4NCg0KVGhlIHBvcnRpb24gb2YgdGhlIHNjYXR0ZXJwbG90IG1hdHJpeCB3ZSdsbCBleHRlbmQgaXMgdGhlIGNvcnJlbGF0aW9uIHZhbHVlcy4gV2hlcmVhcyB0aGUgYGdncGFpcnMoKWAgZnVuY3Rpb24gdXNlcyBhIFBlYXJzb24gY29ycmVsYXRpb24sIHdlIGNhbiBjaG9vc2UgYmV0d2VlbiBQZWFyc29uIG9yIFNwZWFybWFuIGNvcnJlbGF0aW9uLiBTb21ldGltZXMgeW91IG1heSB3aXNoIHRvIGNvbXBhcmUgYm90aCBzaW5jZSBQZWFyc29uIGV4YW1pbmVzIGxpbmVhciByZWxhdGlvbnNoaXBzIHdoZXJlYXMgU3BlYXJtYW4gdXNlcyBhIHJhbmtpbmcgbWV0aG9kIHRvIGNvbXBhcmUgdmFyaWFibGVzIGZvciBtb25vdG9uaWMgcmVsYXRpb25zaGlwcy4NCg0KVG8gZ2VuZXJhdGUgb3VyIG1hdHJpeCB3ZSdsbCBlbXBsb3kgYSBjb3VwbGUgb2YgYWRkaXRpb25hbCBmdW5jdGlvbnM6DQoNCi0gICBgZXhwYW5kLmdyaWQoKWA6IHdlIGNhbiB1c2UgdGhpcyB0byBidWlsZCBhIG1hdHJpeCBvZiBwYWlyLXdpc2UgY29tYmluYXRpb24gdmFsdWVzLiBXZSdsbCB1c2UgdGhlc2UgdG8gZ2VuZXJhdGUgdGhlIHBhaXItd2lzZSBjb2x1bW4gY29tYmluYXRpb25zIHdlIHdhbnQgdG8gY29tcGFyZSB3aXRoIGBjb3IoKWAuDQoNCi0gICBgYXBwbHkoKWA6IGJ5IG5vdyB5b3Ugc2hvdWxkIGJlIGZhbWlsaWFyIHdpdGggdGhpcyBmdW5jdGlvbi4gV2UnbGwgdXNlIGl0IHRvIGl0ZXJhdGUgdGhyb3VnaCBvdXIgcGFpci13aXNlIGNvbHVtbiBjb21iaW5hdGlvbnMgYW5kIHNlbmQgZWFjaCBvZiB0aG9zZSB0by4uLg0KDQotICAgYGNvcigpYDogdGhpcyB3aWxsIHJldHVybiB0aGUgY29ycmVsYXRpb24gY28tZWZmaWNpZW50IGJhc2VkIG9uIHRoZSBgbWV0aG9kYCB3ZSBjaG9vc2UgKHBlYXJzb24sIGtlbmRhbGwsIHNwZWFybWFuKQ0KDQpgYGB7cn0NCiMgRXhhbXBsZSBvZiBob3cgeW91IGNhbiB1c2UgZXhwYW5kLmdyaWQgdG8gbWFrZSBwYWlyd2lzZSBjb21iaW5hdGlvbiBiZXR3ZWVuIHR3byBzZXRzIG9mIGRhdGEuDQpleHBhbmQuZ3JpZChjKC4uLiksYyguLi4pKQ0KYGBgDQoNCmBgYHtyfQ0KIyBGaXJzdCB3ZSBuZWVkIHRvIGZpeCB0aGUgY29sdW1uIG5hbWVzIGZyb20gb3VyIGRhdGENCnd5bGVyX3JlYWRjb3VudHNfb25seS5kZiA8LSAgICAgDQogICAgDQogICAgIyBVc2UgdGhlIEZVTEwgc2V0IG9mIHJlYWRjb3VudCBkYXRhDQogICAgd3lsZXJfcmVhZGNvdW50cy5kZiAlPiUgDQogICAgIyBSZW5hbWUgdGhlIGNvbHVtbnMgYmUgcmVtb3ZpbmcgdGhlIGZpcnN0IHBvcnRpb246IEFFQ0lJX3h4DQogICAgcmVuYW1lX3dpdGgoLiwgfiBzdHJfcmVwbGFjZShzdHJpbmcgPSAueCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gciIoXHcqX1xkKl8pIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlID0gIiIpKSAlPiUgDQogICAgIyBEcm9wIHRoZSBmaXJzdCB0d28gY29sdW1ucyBhcyB3ZWxsDQogICAgZHBseXI6OnNlbGVjdChjKDM6MzgpKQ0KDQojIENyZWF0ZSBvdXIgY29ycmVsYXRpb24gbWF0cml4DQp3eWxlcl9jb3IubXggPC0NCiAgIyBXZSdsbCBhcHBseSBhIGZ1bmN0aW9uIHRvIGVhY2ggcm93IG9mIHRoZSByZXN1bHRpbmcgZ3JpZCBwYWlycw0KICBtYXRyaXgoYXBwbHkoZXhwYW5kLmdyaWQoYyguLi4pLGMoLi4uKSksICAgICAgICAgICAgICAgICAgICAgIyBnZW5lcmF0ZSB0aGUgcGFpci13aXNlIGNvbWJpbmF0aW9ucw0KICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAjIGV4cGxvcmUgaXQgcm93LWJ5LXJvdw0KICAgICAgICAgICAgICAgTUFSR0lOID0gMSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgIyBBcHBseSBhIGZ1bmN0aW9uIHRvIGVhY2ggcm93IHdoaWNoIGdlbmVyYXRlcyBhIHBlYXJzb24gY29ycmVsYXRpb24gYmV0d2Vlbg0KICAgICAgICAgICAgICAgIyBhIHNwZWNpZmljIHBhaXIgb2YgY29sdW1ucw0KICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgY29yKHd5bGVyX3JlYWRjb3VudHNfb25seS5kZlssIHhbMV1dLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3eWxlcl9yZWFkY291bnRzX29ubHkuZGZbLCB4WzJdXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAicGVhcnNvbiIpICAgICAgICAgICAgICAgIyBVc2UgYSBQZWFyc29uIGNvcnJlbGF0aW9uDQogICAgICAgICAgICAgICApLCAgIyBFbmQgdGhlIGFwcGx5IGZ1bmN0aW9uDQogICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgbnJvdyA9IDM2LCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIENhc3Qgb3VyIHJlc3VsdCBhcyBhIG1hdHJpeA0KICAgICAgICAgZGltbmFtZXMgPSBsaXN0KGNvbG5hbWVzKHd5bGVyX3JlYWRjb3VudHNfb25seS5kZiksICAgICAjIG5hbWUgdGhlIHJvd3MgYW5kIGNvbHVtbnMNCiAgICAgICAgICAgICAgICAgICAgICAgICBjb2xuYW1lcyh3eWxlcl9yZWFkY291bnRzX29ubHkuZGYpKQ0KICAgICAgICAgKQ0KDQojIHRha2UgYSBsb29rIGF0IHRoZSBmaW5hbCBtYXRyaXgNCmhlYWQod3lsZXJfY29yLm14KQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMS40LjQgR2VuZXJhdGUgYSBoZWF0bWFwIG9mIHlvdXIgY29ycmVsYXRpb24gbWF0cml4DQoNCk5vdyB0aGF0IHdlJ3ZlIGNvbXBsZXRlZCB0aGUgY29ycmVsYXRpb24gbWF0cml4IGFuZCBpdCBhcHBlYXJzIHRvIGJlIGNvcnJlY3QsIHdlIGNhbiBnZW5lcmF0ZSB0aGUgaGVhdG1hcCBvZiB0aGUgZGF0YSwgYWxsb3dpbmcgaXQgdG8gZ3JvdXAgZGF0YSBiYXNlZCBvbiB0aGUgUGVhcnNvbiBjb3JyZWxhdGlvbiB2YWx1ZXMuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MjB9DQoNCiMgQ3JlYXRlIGEgSGVhdG1hcCBvYmplY3QNCnd5bGVyX2htYXAgPC0gDQogIEhlYXRtYXAod3lsZXJfY29yLm14LCAgICAgICAgICAgICAgICMgU3VwcGx5IG91ciBtYXRyaXggDQogICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgIyBDbHVzdGVyIG9uIGJvdGggcm93cyBhbmQgY29sdW1ucw0KICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsIGNsdXN0ZXJfY29sdW1ucyA9IFRSVUUsICAgDQogICAgICAgICAgDQogICAgICAgICAgY29sID0gdmlyaWRpcygxMDApLA0KICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICMgVXNlIGNvbHVtbl90aXRsZSBhcyB0aGUgdGl0bGUgb2Ygb3VyIGhlYXRtYXANCiAgICAgICAgICBjb2x1bW5fdGl0bGUgPSAiSGVhdG1hcCBvZiBSTkEtU2VxIFBlYXJzb24gY29ycmVsYXRpb24gb24gcmVhZGNvdW50cyIsDQogICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgIyBSb3RhdGUgdGhlIGxlZ2VuZCBob3Jpem9udGFsbHkgYW5kIGdpdmUgaXQgYSB0aXRsZQ0KICAgICAgICAgIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdCh0aXRsZSA9ICJQZWFyc29uIHNjb3JlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kX2RpcmVjdGlvbiA9ICJob3Jpem9udGFsIiksDQoNCiAgICAgICAgICAjIFNldCB0aGUgcm93L2NvbHVtbiBsYWJlbCBmb250IHNpemUNCiAgICAgICAgICByb3dfbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gMTYpLA0KICAgICAgICAgIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxNikNCiAgICAgICAgICApDQoNCiMgUGxvdCB0aGUgaGVhdG1hcCANCmRyYXcod3lsZXJfaG1hcCwgDQogICAgICMgUGxvdCB0aGUgbGVnZW5kIG9uIHRoZSBib3R0b20NCiAgICAgaGVhdG1hcF9sZWdlbmRfc2lkZSA9ICJib3R0b20iDQogICAgKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLjUuMCBNYWtlIGEgY2x1c3RlciBkZW5kcm9ncmFtIHdpdGggYGhjbHVzdCgpYA0KDQpBcyB5b3UgY2FuIHNlZSwgY2x1c3RlcmluZyBvdXIgZGF0YSAob24gdGhlIHJpZ2h0IG1ldHJpYyEpIG1ha2VzIGEgYmlnIGRpZmZlcmVuY2UgaW4gaG93IG91ciBkYXRhIGlzIGRpc3BsYXllZC4gSW4gb3VyIGxhc3QgZmV3IGhlYXRtYXBzLCB3ZSBhbGxvd2VkIGBIZWF0bWFwKClgIHRvIGdlbmVyYXRlIGl0J3Mgb3duIGNsdXN0ZXJpbmcuIFdlIGNvdWxkIGhhdmUgc3VwcGxpZWQgaXQgd2l0aCBhIHNwZWNpZmljIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBkaXN0YW5jZXMsIG9yIGV2ZW4gYSBkZW5kcm9ncmFtIG9iamVjdCB0byBvcmRlciB0aGUgZGF0YSBhcyB3ZWxsLiBVbmRlciB0aGUgaG9vZCwgdGhlIGZ1bmN0aW9uIGlzIGRlZmF1bHRpbmcgdG8gZXVjbGlkZWFuIGRpc3RhbmNlcyBhbmQgYWN0dWFsbHkgcGFzc2luZyB0aGF0IGluZm9ybWF0aW9uIG9uIHRvIHRoZSBgaGNsdXN0KClgIGZ1bmN0aW9uIHRvIHByb2R1Y2UgdGhlIGRlbmRyb2dyYW1zLg0KDQpUaGUgY2hvaWNlIG9mIGBtZXRob2RgIGRldGVybWluZXMgaG93IHRoZSBkYXRhIGlzIGNsdXN0ZXJlZC4gVGhlIGNob2ljZXMgaW5jbHVkZTogd2FyZC5ELCB3YXJkLkQyLCBzaW5nbGUsIGNvbXBsZXRlIChkZWZhdWx0KSwgYXZlcmFnZSwgbWNxdWl0dHksIG1lZGlhbiBhbmQgY2VudHJvaWQuDQoNCkluICpnZW5lcmFsKiAqKndhcmQuRCoqLCAqKndhcmQuRDIqKiBhbmQgKipjb21wbGV0ZSoqIHN0cmF0ZWdpZXMgdHJ5IHRvIGZpbmQgY29tcGFjdCBzbWFsbCAic3BoZXJpY2FsIiBjbHVzdGVycy4gVGhlIHNpbmdsZSBsaW5rYWdlIGFkb3B0cyBhICdmcmllbmRzIG9mIGZyaWVuZHMnIGNsdXN0ZXJpbmcgc3RyYXRlZ3kuIFRoZSBvdGhlciBtZXRob2RzIGNhbiBiZSBzYWlkIHRvIGFpbSBmb3Igc29tZXdoZXJlIGluIGJldHdlZW4uIEZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHRoZXNlLCB5b3UgY2FuIGRpZyBpbnRvIHRoZSBbYGhjbHVzdCgpYCBkb2N1bWVudGF0aW9uXShodHRwczovL3N0YXQuZXRoei5jaC9SLW1hbnVhbC9SLXBhdGNoZWQvbGlicmFyeS9zdGF0cy9odG1sL2hjbHVzdC5odG1sKQ0KDQpXZSBjYW4gdHVybiB0byBgaGNsdXN0KClgIHRvIGdlbmVyYXRlIGp1c3QgZGVuZHJvZ3JhbXMgZm9yIHVzIGJ1dCBwcmlvciB0byB0aGF0IHdlIHN0aWxsIG5lZWQgdG8gcmVmb3JtYXQgb3VyIG1hdHJpeCBhIGxpdHRsZSB1c2luZyB0aGUgYGRpc3QoKWAgZnVuY3Rpb24uDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMS41LjEgQ2FsY3VsYXRlIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIG91ciBwb2ludHMgd2l0aCBgZGlzdCgpYA0KDQpSZW1lbWJlciB3ZSB0YWxrZWQgYWJvdXQgb3VyIGRhdGEgYmVpbmcgaW4gbi1kaW1lbnNpb25hbCBzcGFjZT8gVXNpbmcgdGhvc2UgY29vcmRpbmF0ZXMsIHRoZXJlIGFyZSBhIG51bWJlciBvZiB3YXlzIHRvIGNhbGN1bGF0ZSB0aGUgZGlzdGFuY2UgYmV0d2VlbiBwb2ludHMuIFRoZSBzaW1wbGVzdCBmb3JtIGlzIHRvIGNhbGN1bGF0ZSB0aGUgZXVjbGlkZWFuIGRpc3RhbmNlIGJldHdlZW4gcG9pbnRzIHVzaW5nIHRoZSBnZW5lcmljIGZvcm11bGE6DQoNCiQkZChwLHEpID0gXHNxcnR7KHBfezF9LXFfezJ9KV4yICsgKHBfezJ9LXFfezJ9KV4yICsgXGxkb3RzICsgKHBfe259LXFfe259KV4yfSQkDQoNCmJ1dCB0aGVyZSBhcmUgYSBudW1iZXIgb2Ygb3RoZXIgb3B0aW9ucyBhcyB3ZWxsLiBGb3IgaW5zdGFuY2UgeW91IGNhbiBhbHNvIGNob29zZSB0aGUgbWF4aW11bSBkaXN0YW5jZSBiZXR3ZWVuIHR3byBjb21wb25lbnRzIChzYW1lIGRpbWVuc2lvbikuIFRoZSBgZGlzdCgpYCBmdW5jdGlvbiBjYW4gZ2VuZXJhdGUgdGhlc2UgdmFsdWVzIGZvciB5b3UgdXNpbmcgdGhlIHBhcmFtZXRlciBgbWV0aG9kYCB0byBkZXRlcm1pbmUgaG93IHRoZSBkaXN0YW5jZSB3aWxsIGJlIHVzZWQuIFlvdXIgb3B0aW9ucyBhcmU6IGV1Y2xpZGVhbiwgbWF4aW11bSwgbWFuaGF0dGFuLCBjYW5iZXJyYSwgYmluYXJ5IG9yIG1pbmtvd3NraS4NCg0KVGhlIGBkaXN0KClgIGZ1bmN0aW9uIHdpbGwgcmV0dXJuIGEgYGRpc3RgIG9iamVjdCB3aGljaCBpcyBhIGxvd2VyIHRyaWFuZ2xlIGRpc3RhbmNlIG1hdHJpeCBiZXR3ZWVuIGFsbCBvZiB0aGUgcm93cy9vYnNlcnZhdGlvbnMgdXNpbmcgdGhlIGNvbHVtbnMgYXMgY29vcmRpbmF0ZXMuDQoNCkxldCdzIHJldHVybiB0byBvdXIgUEhVIGRhdGEgaW4gYGNvdmlkX2RlbW9ncmFwaGljc19ub3JtLm14YCB0byB0cnkgaXQgb3V0IGFuZCBzZWUgaG93IGl0IHdvcmtzLg0KDQpgYGB7cn0NCmRpc3QoY292aWRfZGVtb2dyYXBoaWNzX25vcm0ubXhbLC0xXSwgDQogICAgIG1ldGhvZD0iZXVjbGlkZWFuIikgICU+JSBzdHIoKSMgVGhlIGRlZmF1bHQgbWV0aG9kIGlzIGV1Y2xpZGVhbg0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMS41LjIgQ2x1c3RlciB5b3VyIGRpc3RhbmNlIG1hdHJpeA0KDQpOb3cgd2UgYXJlIHJlYWR5IHRvIGNsdXN0ZXIgb3VyIGRpc3RhbmNlIG1hdHJpeCB1c2luZyB0aGUgYGhjbHVzdCgpYCBtZXRob2QNCg0KUGFyYW1ldGVycyB0aGF0IGFyZSBpbXBvcnRhbnQ6DQoNCi0gICBgbWV0aG9kYDogYWxyZWFkeSBkZXNjcmliZWQgYXMgdGhlIG1ldGhvZCB3aGljaCB3ZSB3YW50IHRvIHVzZSB0byBjbHVzdGVyIG91ciBkYXRhLg0KDQogICAgLSAgIHdhcmQuRCwgd2FyZC5EMiwgc2luZ2xlLCBjb21wbGV0ZSAoZGVmYXVsdCksIGF2ZXJhZ2UsIG1jcXVpdHR5LCBtZWRpYW4gYW5kIGNlbnRyb2lkLg0KDQotICAgYGtgOiB0aGUgbnVtYmVyIG9mIGdyb3VwcyB3ZSB3b3VsZCBsaWtlIG91ciBmaW5hbCBkYXRhIHRvIGJlIHNwbGl0IGludG8gLSByZXByZXNlbnRlZCBieSBjb2xvdXJzLg0KDQpgYGB7cn0NCiMgTWFrZSBhIGNsdXN0ZXIgb2JqZWN0IGFuZCB0YWtlIGEgbG9vayBhdCBpdA0KcGh1LmhjIDwtIGhjbHVzdChkaXN0KGNvdmlkX2RlbW9ncmFwaGljc19ub3JtLm14WywtMV0pLCANCiAgICAgICAgICAgICAgICAgbWV0aG9kID0gLi4uKQ0KDQojIFdoYXQgZG9lcyBvdXIgaGNsdXN0IG9iamVjdCBjb250YWluPw0Kc3RyKHBodS5oYykNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDEuNS4zIFBsb3QgeW91ciBjbHVzdGVyIG9iamVjdCB1c2luZyBgZnZpel9kZW5kKClgIGZyb20gdGhlIHBhY2thZ2UgYGZhY3RvZXh0cmFgDQoNCk5vdyB0aGF0IHdlIGhhdmUgZ2VuZXJhdGVkIG91ciBjbHVzdGVyaW5nIG9iamVjdCwgeW91IGNhbiBzZWUgdGhhdCBpdCBob2xkcyBhIG51bWJlciBvZiBmZWF0dXJlcyBpbmNsdWRpbmcgdGhlIGhlaWdodCAobGVuZ3RoKSBvZiBvdXIgYnJhbmNoZXMsIGFuIG9yZGVyaW5nIG9mIG91ciBQSFVzIGFuZCB0aGUgb3JkZXIgaW4gd2hpY2ggdGhleSBtZXJnZS4gVGhlIGBtZXJnZWAgcHJvY2VzcyBpcyBkZXNjcmliZWQgaW4gbW9yZSBkZXRhaWwgYXMgd2VsbCB3aXRoIHRoZSBgaGNsdXN0KClgIFtkb2N1bWVudGF0aW9uXShodHRwczovL3N0YXQuZXRoei5jaC9SLW1hbnVhbC9SLXBhdGNoZWQvbGlicmFyeS9zdGF0cy9odG1sL2hjbHVzdC5odG1sKQ0KDQpUbyBzaW1wbGlmeSB0aGUgcGxvdHRpbmcgcHJvY2Vzcywgd2UgY2FuIHVzZSB0aGUgYGZ2aXpfZGVuZCgpYCBmdW5jdGlvbiB3aGljaCB3aWxsIHBhcnNlIHRocm91Z2ggdGhlIGBoY2x1c3RgIG9iamVjdCB0byBwcm9kdWNlIGEgcHJvcGVyIGRlbmRyb2dyYW0uIFdlIGNhbiBldmVuIHNwZWNpZnkgc29tZSBncm91cGluZyBvciBjb2xvdXJpbmcgc2NoZW1lcyBiYXNlZCBvbiBob3cgbWFueSBncm91cHMsIGBrYCwgd2UgYmVsaWV2ZSBhcmUgcHJlc2VudCBpbiBvdXIgZGF0YS4NCg0KV2UgY2FuIHRyZWF0IHRoZSByZXN1bHRpbmcgcGxvdCBsaWtlIGEgZ2dwbG90IHRvIHVwZGF0ZSBwYXJ0aWN1bGFyIGVsZW1lbnRzIGFzIHdlbGwuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MjB9DQojIFBsb3Qgb3V0IG91ciBkZW5kcm9ncmFtIHdpdGggZnZpel9kZW5kDQoNCmZ2aXpfZGVuZCguLi4sICAgICAgICAgICAgICAgICAgICAgICAgIyBwcm92aWRlIG91ciBoY2x1c3Qgb2JqZWN0DQogICAgICAgICAgY2V4ID0gMS41LCAgICAgICAgICAgICAgICAgICAgICMgc2V0IHRoZSB0ZXh0IHNpemUgdG8gYmUgbGFyZ2VyDQogICAgICAgICAgayA9IC4uLiwgICAgICAgICAgICAgICAgICAgICAgICAgIyBIb3cgbWFueSBjbHVzdGVycyBkbyB3ZSBleHBlY3QgaW4gb3VyIGRhdGEgDQogICAgICAgICAgcGFsZXR0ZSA9ICJTZXQxIiwgICAgICAgICAgICAgICMgd2hhdCBjb2xvdXJzIHdpbGwgd2UgdXNlLiBNYW55IG9wdGlvbnMgaW5jbHVkaW5nIFJCcmV3ZXIgcGFsZXR0ZXMNCiAgICAgICAgICBob3JpeiA9IFRSVUUsDQogICAgICAgICAgbGFiZWxzX3RyYWNrX2hlaWdodCA9IDEyMDAwLA0KICAgICAgICAgKSArDQoNCiAgICAjIEJ1bXAgdXAgdGhlIHRleHQgc2l6ZSBmb3IgbXkgb2xkIGV5ZXMNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCkpIA0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDIuMC4wIENsdXN0ZXJpbmcgZGF0YSBpbnRvIGdyb3Vwcw0KDQpTbyB3ZSd2ZSB2aXN1YWxpemVkIG91ciBkYXRhc2V0IGFzIGEgZGVuZHJvZ3JhbSBhbmQgaXQgZ2l2ZXMgdXMgYW4gaWRlYSBvZiBob3cgdGhlIHNhbXBsZXMgbWlnaHQgYmUgcmVsYXRlZCAoaW4gbi1kaW1lbnNpb24gc3BhY2UpIGJhc2VkIG9uIHRoZSBjYXNlIHZhbHVlcyB3ZSBwcm9kdWNlZCBmcm9tIG5vcm1hbGl6YXRpb24uDQoNCkstbWVhbnMgY2x1c3RlcmluZyBpcyB1bnN1cGVydmlzZWQgbGVhcm5pbmcgZm9yIHVuY2F0ZWdvcml6ZWQgZGF0YS4gV2UgZG9uJ3QgcmVhbGx5IGtub3cgdGhlIHRyYWluaW5nIGxhYmVscyBvZiBvdXIgZGF0YSBhbmQgYXJlIG1vcmUgaW50ZXJlc3RlZCBpbiBzZWVpbmcgd2hpY2ggb25lcyBncm91cCB0b2dldGhlci4gSG93IG1hbnkgY2x1c3RlcnMgc2hvdWxkIHdlIGFpbSB0byBnZW5lcmF0ZT8gV2UndmUgYWxyZWFkeSBzZWVuIHRoYXQgb3VyIGRhdGEgbGlrZWx5IHNwbGl0cyBpbnRvIDMgZ3JvdXBzIGJhc2VkIG9uIHRoZSBoZWF0bWFwcyBhbmQgYGhjbHVzdCgpYCBkYXRhLiBXaWxsIHdlIGdldCB0aGUgc2FtZSB0aGluZyB3aXRoIGEgay1tZWFucyBtZXRob2Q/DQoNCldoZW4gaW4gZG91YnQsIHlvdSBjYW4gcXVpY2tseSBjb25zdWx0IHRoZSBgZmFjdG9NaW5lcmAgZnVuY3Rpb24gYGZ2aXpfbmJjbHVzdCgpYCB3aGljaCBjYW4gZ3Vlc3MgaG93IG1hbnkgY2x1c3RlcnMgYXJlIGlkZWFsIGZvciByZXByZXNlbnRpbmcgeW91ciBkYXRhLiBOb3RlIHRoYXQgaXQgbWF5IG5vdCBwcm9kdWNlIHlvdXIgaWRlYWwgbnVtYmVyIG9mIGNsdXN0ZXJzIGJ1dCBpZiB5b3UncmUgc3R1Y2ssIHRoaXMgaXMgYSBnb29kIHN0YXJ0Lg0KDQpUaGVyZSBhcmUgdGhyZWUgYG1ldGhvZGAgb3B0aW9uczogd3NzLCBnYXBfc3RhdCwgYW5kIHNpbGhvdWV0dGUgd2hpY2ggYWxsIGFpbSB0byBtaW5pbWl6ZSB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzLg0KDQotICAgYHdzc2AgZWxib3cgbWV0aG9kIGFpbXMgdG8gbWluaW1pemUgaW50cmFjbHVzdGVyIHZhcmlhdGlvbi4gSG93IGNvbXBhY3QgaXMgb3VyIGNsdXN0ZXJpbmc/IEEgbG93ZXIgV1NTIGlzIGJldHRlci4NCg0KLSAgIGBzaWxob3VldHRlYCBtZWFzdXJlcyBob3cgd2VsbCBhbiBvYmplY3QgbGllcyB3aXRoaW4gaXQncyBjbHVzdGVyIGJ5IGNvbXB1dGluZyB0aGUgYXZlcmFnZSBkaXN0YW5jZSB0byBvdGhlciBtZW1iZXJzIGluIGl0cyBvd24sICRhKGkpJCB2cyBvdGhlciBjbHVzdGVycyAkYihpKSQuIFdlIGV2YWx1YXRlICRcZnJhY3tiKGkpIC0gYShpKX17bWF4XHthKGkpLCBiKGkpfSQgd2l0aCBoaWdoZXIgdmFsdWVzIGluZGljYXRpbmcgYmV0dGVyIGNsdXN0ZXJpbmcuDQoNCi0gICBgZ2FwX3N0YXRgIGFsc28gY29tcGFyZXMgdG90YWwgaW50cmEtY2x1c3RlciB2YXJpYXRpb24gYnV0IGFnYWluc3QgYSBudWxsIHJlZmVyZW5jZSBkaXN0cmlidXRpb24uIFRoZSBvcHRpbWFsIHZhbHVlIGF0dGVtcHRzIHRvIGNob29zZSB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIHN1Y2ggdGhhdCB0aGUgc21hbGxlc3QgYGtgIGdhcCBzdGF0aXN0aWMgaXMgd2l0aGluIG9uZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIGdhcCBhdCBgaysxYC4NCg0KYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTZ9DQojIEhvdyBtYW55IGNsdXN0ZXJzIHNob3VsZCB3ZSBnZW5lcmF0ZT8NCmZ2aXpfbmJjbHVzdCh4ID0gY292aWRfZGVtb2dyYXBoaWNzX25vcm0ubXhbLC0xXSwgICAjIFByb3ZpZGUgdGhlIGRhdGFzZXQNCiAgICAgICAgICAgICBGVU5jbHVzdGVyID0gIGttZWFucywgICAgICAgICAgICAgICAgICAjIEhvdyB3aWxsIHlvdSBjbHVzdGVyIHRoZSBkYXRhPw0KICAgICAgICAgICAgIG1ldGhvZD0uLi4pICsgICAgICAgICAgICAgICAgICAgICAgICAjIFdoYXQgbWV0aG9kIHdpbGwgeW91IHVzZT8NCiAgDQogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpDQpgYGANCg0KYGBge3J9DQojIEhvdyBtYW55IGNsdXN0ZXJzIHNob3VsZCB3ZSBnZW5lcmF0ZT8NCmZ2aXpfbmJjbHVzdCh4ID0gY292aWRfZGVtb2dyYXBoaWNzX25vcm0ubXhbLC0xXSwgICAjIFByb3ZpZGUgdGhlIGRhdGFzZXQNCiAgICAgICAgICAgICBGVU5jbHVzdGVyID0gIGttZWFucywgICAgICAgICAgICAgICAgICAjIEhvdyB3aWxsIHlvdSBjbHVzdGVyIHRoZSBkYXRhPw0KICAgICAgICAgICAgIG1ldGhvZD0ic2lsaG91ZXR0ZSIpICsgICAgICAgICAgICAgICAgICMgV2hhdCBtZXRob2Qgd2lsbCB5b3UgdXNlPw0KICANCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkNCmBgYA0KDQpgYGB7cn0NCiMgSG93IG1hbnkgY2x1c3RlcnMgc2hvdWxkIHdlIGdlbmVyYXRlPw0KZnZpel9uYmNsdXN0KHggPSBjb3ZpZF9kZW1vZ3JhcGhpY3Nfbm9ybS5teFssLTFdLCAgICMgUHJvdmlkZSB0aGUgZGF0YXNldA0KICAgICAgICAgICAgIEZVTmNsdXN0ZXIgPSAga21lYW5zLCAgICAgICAgICAgICAgICAgICMgSG93IHdpbGwgeW91IGNsdXN0ZXIgdGhlIGRhdGE/DQogICAgICAgICAgICAgbWV0aG9kPSJnYXBfc3RhdCIpICsgICAgICAgICAgICAgICAgICAgIyBXaGF0IG1ldGhvZCB3aWxsIHlvdSB1c2U/DQogIA0KICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTApKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAyLjEuMCBHZW5lcmF0ZSB5b3VyIGNsdXN0ZXJzIHVzaW5nIGBrbWVhbnMoKWANCg0KU28gZnJvbSB0aHJlZSBkaWZmZXJlbnQgYW5hbHlzZXMgd2UgZGlkbid0IHJlYWxseSBnZXQgYSBjb25jZW5zdXMgb24gdGhlIGJlc3QgbnVtYmVyIG9mIGNsdXN0ZXJzOg0KDQotICAgd3NzOiBPdXIgZWxib3cgbGV2ZWwgb3V0IGF0IGFyb3VuZCBrPTMgb3Igaz00DQoNCi0gICBzaWxob3VldHRlOiBPdXIgYmlnZ2VzdCBqdW1wIGlzIGF0IGs9MiB3aGljaCBpcyBhbHNvIG91ciBiaWdnZXN0IHZhbHVlDQoNCi0gICBnYXBfc3RhdDogVGhlIGJpZ2dlc3QganVtcCBpbiB3aXRoaW4tY2x1c3RlciBkaXN0YW5jZSBpcyBhdCBrPTMgYWx0aG91Z2ggd2Ugc2VlIGFub3RoZXIganVtcCBhZ2FpbiBhdCBrPTUNCg0KYnV0IGl0IGxvb2tzIGxpa2UgdGhlIGFuc3dlciByYW5nZXMgYmV0d2VlbiAyIGFuZCA1IGNsdXN0ZXJzLiBOb3RlIHRoYXQgaWYgeW91ciBkYXRhIGNvbnNpc3RlbnRseSBzdWdnZXN0IGsgPSAxLCB0aGVuIHlvdSBzaG91bGRuJ3QgY2x1c3RlciBhdCBhbGwhIFNpbmNlIG91ciBkYXRhIGFscmVhZHkgc3VnZ2VzdHMgMyBjbHVzdGVycywgbGV0J3Mgc3RhcnQgd2l0aCB0aGF0LiBXZSdsbCB1c2UgdGhlIGBrbWVhbnMoKWAgZnVuY3Rpb24gdG8gYWNjb21wbGlzaCB0aGlzLg0KDQpTaW1pbGFyIGluIGlkZWEgdG8gYGhjbHVzdCgpYCwgdGhlIGBrbWVhbnMoKWAgYWxnb3JpdGhtIGF0dGVtcHRzIHRvIGdlbmVyYXRlIGstcGFydGl0aW9uZWQgZ3JvdXBzIGZyb20gdGhlIGRhdGEgc3VwcGxpZWQgd2l0aCBhbiBlbXBoYXNpcyBvbiBtaW5pbWl6aW5nIHRoZSBzdW0gb2Ygc3F1YXJlcyBmcm9tIHBvaW50cyB0byB0aGUgYXNzaWduZWQgY2x1c3RlciBjZW50ZXJzLiBUaGlzIGRpZmZlcnMgZnJvbSBgaGNsdXN0KClgIHdoaWNoIGZpbmlzaGVzIGJ1aWxkaW5nIHRoZSBlbnRpcmUgcmVsYXRpb25zaGlwIHZpYSBkZW5kcm9ncmFtIHdpdGhvdXQgYWN0dWFsbHkgY2hvb3NpbmcgImNsdXN0ZXJzIi4NCg0KV2UncmUgZ29pbmcgdG8gYWxzbyB1c2UgYHNldC5zZWVkKClgIGZvciBvdXIgcmFuZG9tIG51bWJlciBnZW5lcmF0aW9uLiBXZSB0ZW5kIHRvIHRoaW5rIG9mIHRoaW5ncyBhcyByYW5kb20sIGJ1dCBjb21wdXRhdGlvbmFsbHksIHJhbmRvbW5lc3MgaXMgYnVpbHQgb24gYWxnb3JpdGhtcy4gVGhlcmVmb3JlIHdlIGNhbiAicmVjcmVhdGUiIG91ciByYW5kb21uZXNzIGlmIHdlIHVzZSBhIHByZS1kZXRlcm1pbmVkIHN0YXJ0aW5nIHBvaW50IGFsc28ga25vd24gYXMgdGhlICJzZWVkIi4NCg0KVGhlIHBhcmFtZXRlcnMgd2UncmUgaW50ZXJlc3RlZCBpbiB1c2luZyB3aXRoIGBrbWVhbnMoKWAgYXJlOg0KDQotICAgYHhgIHRoZSAqbnVtZXJpYyBtYXRyaXgqIG9mIG91ciBkYXRhDQoNCi0gICBgY2VudGVyc2AgZGV0ZXJtaW5lcyB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIHdlIHdhbnQgdG8gcHJvZHVjZQ0KDQotICAgYG5zdGFydGAgZGVmaW5lcyBob3cgbWFueSByYW5kb21seSBjaG9zZW4gc2V0cyBvZiBrIGNlbnRyZXMgeW91J2xsIHVzZSB0byBzdGFydCB0aGUgYW5hbHlzaXMuIENob29zaW5nIG11bHRpcGxlIGRpZmZlcmVudCBzZXRzIGFsbG93cyB0aGUgYWxnb3JpdGhtIHRvIGF2b2lkIGxvY2FsIG1pbmltYS4NCg0KLSAgIGBpdGVyLm1heGAgaXMgdGhlIG1heCBudW1iZXIgb2YgaXRlcmF0aW9ucyBhbGxvd2VkIHdoaWxlIHRyeWluZyB0byBjb252ZXJnZSBvbiB0aGUgYmVzdCBtaW5pbXVtIG1ldHJpYw0KDQpgYGB7cn0NCiMgQ29tcHV0ZSBrLW1lYW5zIHdpdGggayA9IDMNCg0KIyBTZXQgYSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkuDQpzZXQuc2VlZCgxMjMpDQoNCiMgR2VuZXJhdGUgb3VyIGstbWVhbnMgYW5hbHlzaXMNCnBodV9jYXNlcy5rbSA8LSANCiAga21lYW5zKC4uLiwgIyBXZSdsbCBzY2FsZSBvdXIgZGF0YSBmb3IgdGhpcyAobW9yZSBvbiB0aGF0IGxhdGVyIHRvbyEpDQogICAgICAgICBjZW50ZXJzID0gMywgDQogICAgICAgICBuc3RhcnQgPSAyNSwNCiAgICAgICAgIGl0ZXIubWF4ID0gNTAwKQ0KYGBgDQoNCmBgYHtyfQ0KIyBXaGF0IGlzIHRoZSBzdHJ1Y3R1cmUgb2Ygb3VyIGttZWFucyBvYmplY3Q/DQpzdHIocGh1X2Nhc2VzLmttKQ0KYGBgDQoNCmBgYHtyfQ0KIyBLLW1lYW5zIGNsdXN0ZXJzIHNob3dpbmcgdGhlIGdyb3VwIG9mIGVhY2ggaW5kaXZpZHVhbHMNCnBodV9jYXNlcy5rbSQuLi4NCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMi4yLjAgUGxvdCB5b3VyIGstbWVhbnMgcmVzdWx0cyB3aXRoIGBmdml6X2NsdXN0ZXIoKWANCg0KTm90aWNlIHRoZSBzdHJ1Y3R1cmUgb2YgdGhpcyBvdXRwdXQ/IEl0IGluY2x1ZGVzIGBjbHVzdGVyYCB3aGljaCBkZW5vdGVzIHdoaWNoIHJvdyBiZWxvbmdzIHRvIGVhY2ggb2YgdGhlIDMgY2x1c3RlcnMgY2hvc2VuLiBUaGUgY2VudHJlIG9mIGVhY2ggY2x1c3RlciBpcyBkZWZpbmVkIGJ5IGBjZW50ZXJzYCB3aGljaCBpbiB0aGlzIGNhc2UgaXMgYSAzeDcgYXJyYXkgd2hlcmUgZWFjaCByb3cgdXNlcyA3IHZhbHVlcyB0byBkZWZpbmUgdGhlaXIgcG9zaXRpb24gd2l0aGluIG91ciA3LWRpbWVuc2lvbiBkYXRhc2V0LiBXZSBjYW4gc2VlIGVhY2ggY2x1c3RlcidzIGBzaXplYCBpcyAxOCwgMTEsIGFuZCA1IFBIVXMgcmVzcGVjdGl2ZWx5LiBOb3RpY2UsIGhvd2V2ZXIsIHRoYXQgbm9uZSBvZiB0aGUgb3JpZ2luYWwgY29vcmRpbmF0ZXMgZm9yIG91ciBkYXRhIGFyZSByZXRhaW5lZCBpbiB0aGlzIG9iamVjdC4NCg0KUmF0aGVyIHRoYW4gdXNlIGBnZ3Bsb3RgIGRpcmVjdGx5LCB3ZSdsbCB1c2UgdGhlIGdncGxvdC1jb21wYXRpYmxlIGBmdml6X2NsdXN0ZXIoKWAgZnVuY3Rpb24gdG8gaGVscCB1cyB0cmFuc2Zvcm0gdGhlIHZhbHVlcyBvZiBvdXIgcG9pbnRzIGZyb20gYSA3LWRpbWVuc2lvbiBjb29yZGluYXRlIHN5c3RlbSB0byBhIDItZGltZW5zaW9uIHZpc3VhbCBmb3JtYXQgc3VpdGFibGUgZm9yIG91ciBzaW1wbGVyIGJyYWlucy4gVW5kZXIgdGhlIGhvb2QgYGZ2aXpfY2x1c3RlcigpYCBpcyBwZXJmb3JtaW5nIGEgcHJpbmNpcGxlIGNvbXBvbmVudCBhbmFseXNpcyB0byBncm91cCBvdXIgYGRhdGFgIGFsb25nIHRoZSBmaXJzdCB0d28gcHJpbmNpcGFsIGNvbXBvbmVudHMgKG1vcmUgb24gd2hhdCB0aGF0IG1lYW5zIGxhdGVyIHRvbyEpLg0KDQpUaGUgcGFyYW1ldGVycyB3ZSBzaG91bGQgYmUgY29uY2VybmVkIHdpdGggaW4gdGhpcyBmdW5jdGlvbiBpbmNsdWRlOg0KDQotICAgYG9iamVjdGAgdGhlIHBhcnRpdGlvbmluZyBvYmplY3QgZm9yIG91ciBkYXRhLiBJbiB0aGUgY2FzZSBvZiBrLW1lYW5zLCBpdCBpcyBvdXIga21lYW5zIG9iamVjdC4NCg0KLSAgIGBkYXRhYCBpcyB0aGUgb3JpZ2luYWwgZGF0YXNldCB3ZSB1c2VkIHRvIGdlbmVyYXRlIHRoZSBkYXRhLiBUaGlzIHdpbGwgYmUgbmVjZXNzYXJ5IHRvIHBsb3QgdGhlIG90aGVyIGRhdGEgcG9pbnRzLg0KDQotICAgYGVsbGlwc2UudHlwZWAgZGV0ZXJtaW5lcyBob3cgd2UnbGwgb3V0bGluZSBvdXIgY2x1c3RlcnMuIFRoaXMgY29tZXMgd2l0aCBhIG51bWJlciBvZiBvcHRpb25zIGluY2x1ZGluZzoNCg0KICAgIC0gICBjb252ZXg6IGRyYXdzIGJvdW5kYXJpZXMgYmFzZWQgb24gdGhlIG91dGVyIHBvaW50cyBpbiB5b3VyIGNsdXN0ZXINCiAgICAtICAgY29uZmlkZW5jZTogcHJvZHVjZXMgYSBjb25maWRlbmNlIGVsbGlwc2UgYXJvdW5kIHRoZSBjbHVzdGVyIGNlbnRyZXMuIFRoaXMgY2FuIGJlIGZ1cnRoZXIgYWRqdXN0ZWQgdXNpbmcgdGhlIHBhcmFtZXRlciBgZWxsaXBzZS5sZXZlbGAgd2hvc2UgZGVmYXVsdCBpcyAwLjk1Lg0KICAgIC0gICB0LCBub3JtOiBhc3N1bWUgbXVsdGl2YXJpYXRlIHQtZGlzdHJpYnV0aW9uIGFuZCBtdWx0aXZhcmlhdGUgbm9ybWFsIGRpc3RyaWJ1dGlvbnMgdG8gcHJvZHVjZSB0aGVpciBlbGxpcHNlcyB1c2luZyBgZWxsaXBzZS5sZXZlbGAgdG8gc2V0IHRoZWlyIHJhZGl1cy4NCiAgICAtICAgZXVjbGlkOiBzZXRzIGEgY2lyY2xlIG9mIHJhZGl1cyBgZWxsaXBzZS5sZXZlbGAgYXJvdW5kIHRoZSBjZW50cmUgb2YgZWFjaCBjbHVzdGVyLg0KDQpMZXQncyBzZWUgaG93IG91ciBkYXRhIGhhcyBiZWVuIHBhcnRpdGlvbmVkIGJ5IHRoZSBrLW1lYW5zIGNsdXN0ZXJpbmcuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTV9DQoNCiMgUGxvdCB0aGUgY2x1c3Rlcg0KIyBZb3UgY2FuIHRyZWF0IHRoaXMgb2JqZWN0IGxpa2UgYSBnZ3Bsb3Qgb2JqZWN0IGFmdGVyd2FyZHMNCg0KcGh1X2ttZWFucy5wbG90IDwtDQoNCiAgZnZpel9jbHVzdGVyKG9iamVjdCA9IC4uLiwgIyBvdXIgay1tZWFucyBvYmplY3QNCiAgICAgICAgICAgICAgIGRhdGEgPSAuLi4sICMgT3VyIG9yaWdpbmFsIGRhdGEgbmVlZGVkIGZvciBQQ0EgdG8gdmlzdWFsaXplDQogICAgICAgICAgICAgICBlbGxpcHNlLnR5cGUgPSAiY29udmV4IiwgDQogICAgICAgICAgICAgICBnZ3RoZW1lID0gdGhlbWVfYncoKSwgDQogICAgICAgICAgICAgICByZXBlbD1UUlVFLCAjIFRyeSB0byBhdm9pZCBvdmVybGFwcGluZyB0ZXh0DQogICAgICAgICAgICAgICBsYWJlbHNpemUgPSAyMCwNCiAgICAgICAgICAgICAgIHBvaW50c2l6ZSA9IDQsDQogICAgICAgICAgICAgICBtYWluID0gIkstbWVhbnMgY2x1c3RlcmluZyBvZiBQSFUgYnkgbm9ybWFsaXplZCBjYXNlIG51bWJlciINCiAgICAgICAgICAgICAgICkgKw0KDQogICMgU2V0IHNvbWUgZ2dwbG90IHRoZW1lIGluZm9ybWF0aW9uDQogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCkpICsNCg0KICAjIFNldCB0aGUgY29sb3VyIGFuZCBmaWxsIHNjaGVtZSB0byB2aXJpZGlzDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKCkgKw0KICBzY2FsZV9jb2xvdXJfdmlyaWRpc19kKCkNCg0KIyBQcmludCB0aGUgcGxvdCENCnBodV9rbWVhbnMucGxvdA0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAyLjMuMCBDbHVzdGVyaW5nIHlvdXIgUk5BLVNlcSBkYXRhDQoNCkxldCdzIHJldHVybiB0byB0aGUgKld5bGVyIGV0IGFsLiwgMjAyMCogcmVhZGNvdW50IGRhdGEgYW5kIHRha2UgYSBsb29rIGF0IGl0IHRocm91Z2ggYSBkaWZmZXJlbnQgbGVucy4gV2hlcmVhcyBiZWZvcmUgd2UgaGFkIGdlbmVyYXRlZCBhIGhlYXRtYXAgb2YgdGhlIGRhdGEgYmFzZWQgb24gbG9va2luZyBhdCBnZW5lcyBleHByZXNzZWQgd2l0aGluIGEgcmVhZGNvdW50IHJhbmdlLCB3ZSdsbCBub3cgdGFrZSBhIGRpZmZlcmVudCBhcHByb2FjaC4NCg0KTGV0J3MgbG9vayBhdCB0aGUgdG9wIDUwMCB2YXJpYWJsZSBnZW5lcyBpbiB0aGUgZGF0YXNldCB0byBoZWxwIGNsdXN0ZXIgdGhlbS4gVG8gYWNjb21wbGlzaCB0aGF0IHdlJ2xsIHdhbnQgdG8gZ2VuZXJhdGUgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBpbiByZWFkIGNvdW50IGZvciBlYWNoIHJvdyAoZ2VuZSkuIFdlJ2xsIHV0aWxpemUgYSBmZXcgdmVyYnMgd2UgaGF2ZW4ndCB1c2VkIGJlZm9yZSBpbiB0aGlzIGNsYXNzOg0KDQotICAgYHJvd3dpc2UoKWA6IGluIHRoZSBzYW1lIGNsYXNzIG9mIGZ1bmN0aW9ucyBhcyBgZ3JvdXBfYnkoKWAsIHRoaXMgd2lsbCBzdWJ0bHkgYWx0ZXIgdGhlIHRpYmJsZSBzbyB0aGF0IHdoZW4gd2UgcGVyZm9ybSBtYXRoIG9wZXJhdGlvbnMgb24gaXQsIHRoZXNlIHdpbGwgYmUgY29tcGxldGVkIG9uIGEgcm93LWJ5LXJvdyBiYXNpcy4NCg0KLSAgIGBjX2Fjcm9zcygpYDogdGhpcyBoZWxwZXIgdmVyc2lvbiBvZiBjb21iaW5lIChgYygpYCkgY29tYmluZXMgdmFsdWVzIGFjcm9zcyBjb2x1bW5zIHdpdGhpbiBvdXIgdGliYmxlLg0KDQpgYGB7cn0NCiMgUmV2aWV3IHRoZSB3eWxlciByZWFkY291bnQgZGF0YQ0KaGVhZCh3eWxlcl9yZWFkY291bnRzLmRmKQ0KYGBgDQoNCmBgYHtyfQ0KIyBTYXZlIG91ciByZXN1bHRzIGludG8gYSBuZXcgZGF0YWZyYW1lDQp3eWxlcl9yZWFkY291bnRzX2ZpbHRlcmVkLmRmIDwtDQogIA0KICAjIFN0YXJ0IHdpdGggdGhlIG9yaWdpbmFsIHJlYWQgY291bnQgZGF0YQ0KICB3eWxlcl9yZWFkY291bnRzLmRmICU+JSANCg0KICAjIFJlbmFtZSB0aGUgY29sdW1ucyBieSByZW1vdmluZyB0aGUgZmlyc3QgcG9ydGlvbjogQUVDSUlfeHgNCiAgcmVuYW1lX3dpdGgoLiwgfiBzdHJfcmVwbGFjZShzdHJpbmcgPSAueCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9IHIiKFx3Kl9cZCpfKSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2UgPSAiIikpICU+JSANCg0KICAjIGZpbHRlciBvdXQgdGhlIGxvdyByZWFkY291bnQgZGF0YS4gTG93IHZhbHVlcyB3aWxsIGNyZWF0ZSB3aWxkIHZhcmlhbmNlIGVhc2lseQ0KICBmaWx0ZXIoaWZfYWxsKC5jb2xzID0gLTEsIC5mbnMgPSB+IC54ID4gMTApKSAlPiUgDQoNCiAgIyBQcmVwYXJlIHRvIGRvIHJvdy13aXNlIGNhbGN1bGF0aW9ucw0KICAuLi4gJT4lIA0KDQogICMgQ2FsY3VsYXRlIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gYWNyb3NzIHJvd3MNCiAgbXV0YXRlKHN0ZGV2ID0gc2QoLi4uKSkgJT4lIA0KICANCiAgIyBVbmdyb3VwIGFuZCBzb3J0IHRoZSBkYXRhIGJ5IGRlc2NlbmRpbmcgdmFsdWUNCiAgdW5ncm91cCgpICU+JSANCiAgYXJyYW5nZShkZXNjKHN0ZGV2KSkgJT4lIA0KICANCiAgIyBUYWtlIHRoZSB0b3AgNTAwIG1vc3QgdmFyaWFibGUgZ2VuZXMNCiAgZHBseXI6OnNsaWNlKDE6NTAwKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpOb3cgd2UnbGwganVzdCBjb252ZXJ0IG91ciBkYXRhZnJhbWUgaW50byBhIG1hdHJpeCBvZiB2YWx1ZXMgc28gd2UgY2FuIHBlcmZvcm0gb3VyIHZhcmlvdXMgYW5hbHlzZXMuDQoNCmBgYHtyfQ0KIyBTYXZlIGp1c3QgdGhlIFJOQS1TZXEgZGF0YSBpbnRvIGEgbWF0cml4DQp3eWxlcl9yZWFkY291bnRzLm14IDwtIGFzLm1hdHJpeCh3eWxlcl9yZWFkY291bnRzX2ZpbHRlcmVkLmRmWywgMzozOF0pDQoNCiMgU2V0IHRoZSByb3cgbmFtZXMgdXNpbmcgdGhlIGluZm9ybWF0aW9uIGZyb20gdGhlIGRhdGEgZnJhbWUNCnJvd25hbWVzKHd5bGVyX3JlYWRjb3VudHMubXgpIDwtIHd5bGVyX3JlYWRjb3VudHNfZmlsdGVyZWQuZGYkZ2VuZQ0KDQojIFRha2UgYSBxdWljayBsb29rIGF0IHRoZSByZXN1bHRpbmcgbWF0cml4IGFuZCBpdHMgcHJvcGVydGllcw0KaGVhZCh3eWxlcl9yZWFkY291bnRzLm14KQ0Kc3RyKHd5bGVyX3JlYWRjb3VudHMubXgpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAyLjMuMSBDcmVhdGUgYSBoZWF0bWFwIG9mIHRoZSBtb3N0IHZhcmlhYmxlIGdlbmVzDQoNClNpbmNlIHdlJ3JlIGhlcmUsIGxldCdzIHRha2UgYSBxdWljayBzdGVwIGJhY2sgYW5kIGxvb2sgYXQgdGhlIGhlYXRtYXAgZnJvbSBvdXIgbmV3IGRhdGFzZXQuIERvZXMgaXQgcmV2ZWFsIGFudGhpbmcgdG8gdXMgbm93IHRoYXQgaXQgaXMgbW9yZSBudWFuY2VkPw0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTIwfQ0KIyBDcmVhdGUgYSBIZWF0bWFwIG9iamVjdA0Kd3lsZXJfaG1hcCA8LSANCiAgSGVhdG1hcCguLi4sICAgICAgICAgICAgICAgIyBTdXBwbHkgb3VyIG1hdHJpeCANCiAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLCBjbHVzdGVyX2NvbHVtbnMgPSBUUlVFLCAgICMgQ2x1c3RlciBvbiBib3RoIHJvd3MgYW5kIGNvbHVtbnMNCiAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICBjb2wgPSB2aXJpZGlzKDEwMCksDQogICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgIyBVc2UgY29sdW1uX3RpdGxlIGFzIHRoZSB0aXRsZSBvZiBvdXIgaGVhdG1hcA0KICAgICAgICAgIGNvbHVtbl90aXRsZSA9ICJIZWF0bWFwIG9mIFJOQS1TZXEgcmVhZGNvdW50cyBvbiB0b3AgNTAwIG1vc3QgdmFyaWFibGUgZ2VuZXMiLA0KICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICMgUm90YXRlIHRoZSBsZWdlbmQgaG9yaXpvbnRhbGx5IGFuZCBnaXZlIGl0IGEgdGl0bGUNCiAgICAgICAgICBoZWF0bWFwX2xlZ2VuZF9wYXJhbSA9IGxpc3QodGl0bGUgPSAicmVhZGNvdW50cyBwZXIgZ2VuZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZF9kaXJlY3Rpb24gPSAidmVydGljYWwiKSwNCiAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAjIFJlbW92ZSB0aGUgcm93IG5hbWVzDQogICAgICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgIyBTZXQgdGhlIGRlbmRyb2dyYW0gc2l6ZXMNCiAgICAgICAgICAuLi4gPSB1bml0KDQwLCAibW0iKSwNCiAgICAgICAgICAuLi4gPSB1bml0KDIwLCAibW0iKQ0KICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgKQ0KDQojIFBsb3QgdGhlIGhlYXRtYXAgDQpkcmF3KHd5bGVyX2htYXAsIA0KICAgICAjIFBsb3QgdGhlIGxlZ2VuZCBvbiB0aGUgYm90dG9tDQogICAgIGhlYXRtYXBfbGVnZW5kX3NpZGUgPSAibGVmdCINCiAgICApDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAyLjMuMiBHZW5lcmF0ZSBhIGstbWVhbnMgY2x1c3RlciBhbmFseWlzDQoNCkxvb2tzIGxpa2UgdGhlIGhlYXRtYXAgc3RpbGwgZG9lc24ndCByZXZlYWwgYSBsb3Qgb2YgaW5mb3JtYXRpb24gdG8gdXMgbGlrZSBob3cgc2FtcGxlcyBtaWdodCBiZSBncm91cGVkLiBMZXQncyBwcm9jZWVkIHdpdGggay1tZWFucyBhbmQgc2VlIGlmIHRoYXQgd29ya3MgYmV0dGVyLiBXZSBqdXN0IG5lZWQgdG8gcmVwZWF0IG91ciBzdGVwcyBvbiBhIGRpZmZlcmVudCBzZXQgb2YgZGF0YS4gU2luY2Ugd2Ugbm93IGhhdmUgb3VyIG1vc3QgZGl2ZXJzZSBnZW5lcyBhbmQgdGhlaXIgcmVhZGNvdW50cywgd2UnbGwgdGFrZSBhIGxvb2sgYXQgaWYgd2UgY2FuIGNsdXN0ZXIgdGhpcyBpbmZvcm1hdGlvbi4NCg0KMS4gIEdlbmVyYXRlIGFuIGVzdGltYXRlIG9uIHRoZSBhcHByb3ByaWF0ZSBudW1iZXIgb2YgY2x1c3RlcnMNCjIuICBDcmVhdGUgb3VyIGstbWVhbnMgY2x1c3RlciBvYmplY3QNCjMuICBWaXN1YWxpemUgdGhlIGstbWVhbnMgb2JqZWN0IGFuZCBzZWUgd2hpY2ggZXhwZXJpbWVudHMgdGVuZCB0byBncm91cCB0b2dldGhlci4NCg0KYGBge3J9DQojIEhvdyBtYW55IGNsdXN0ZXJzIHNob3VsZCB3ZSBnZW5lcmF0ZT8NCmZ2aXpfbmJjbHVzdCh0KHd5bGVyX3JlYWRjb3VudHMubXgpLCBGVU5jbHVzdGVyID0ga21lYW5zLCBtZXRob2Q9IndzcyIpDQpmdml6X25iY2x1c3QodCh3eWxlcl9yZWFkY291bnRzLm14KSwgRlVOY2x1c3RlciA9IGttZWFucywgbWV0aG9kPSJzaWxob3VldHRlIikNCmZ2aXpfbmJjbHVzdCh0KHd5bGVyX3JlYWRjb3VudHMubXgpLCBGVU5jbHVzdGVyID0ga21lYW5zLCBtZXRob2Q9ImdhcF9zdGF0IikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KRnJvbSB0aGUgMyBtZXRob2RzIHdlIHNlZSB0aGF0DQoNCjEuICBPdXIgV1NTIG1ldGhvZCBwcm9kdWNlZCBhbiBlbGJvdyBhcm91bmQgayA9IDQNCg0KMi4gIE91ciBzaWxob3VldHRlIG1ldGhvZCBwZWFrcyBhdCBrID0gNA0KDQozLiAgT3VyIGdhcF9zdGF0IG1ldGhvZCBzZWVzIGRpbWluaXNoaW5nIHByb2dyZXNzIGFsc28gYXQgayA9IDQNCg0KTGV0J3MgcHJvY2VlZCB3aXRoIHRoYXQgaW4gbWluZCENCg0KYGBge3J9DQojIENvbXB1dGUgay1tZWFucyB3aXRoIGsgPSA0DQoNCiMgU2V0IGEgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5Lg0Kc2V0LnNlZWQoMTIzKQ0KDQojIEdlbmVyYXRlIG91ciBrLW1lYW5zIGFuYWx5c2lzDQp3eWxlcl9yZWFkY291bnRzLmttIDwtIA0Ka21lYW5zKHNjYWxlKC4uLiksICMgV2UnbGwgc2NhbGUgb3VyIGRhdGEgZm9yIHRoaXMgKG1vcmUgb24gdGhhdCBsYXRlciB0b28hKQ0KICAgICAgIGNlbnRlcnMgPSA0LCANCiAgICAgICBuc3RhcnQgPSAyNSwNCiAgICAgICBpdGVyLm1heCA9IDUwMCkNCg0KIyBUYWtlIGEgbG9vayBhdCB0aGUgcmVzdWx0aW5nIGstbWVhbnMgb2JqZWN0DQpzdHIod3lsZXJfcmVhZGNvdW50cy5rbSkNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTE1fQ0KDQojIFBsb3QgdGhlIGNsdXN0ZXINCiMgWW91IGNhbiB0cmVhdCB0aGlzIG9iamVjdCBsaWtlIGEgZ2dwbG90IG9iamVjdCBhZnRlcndhcmRzDQoNCnd5bGVyX2ttZWFucy5wbG90IDwtDQoNCiAgZnZpel9jbHVzdGVyKG9iamVjdCA9IC4uLiwgIyBvdXIgay1tZWFucyBvYmplY3QNCiAgICAgICAgICAgICAgIGRhdGEgPSB0KHd5bGVyX3JlYWRjb3VudHMubXgpLCAjIE91ciBvcmlnaW5hbCBkYXRhIG5lZWRlZCBmb3IgUENBIHRvIHZpc3VhbGl6ZQ0KICAgICAgICAgICAgICAgZWxsaXBzZS50eXBlID0gImNvbnZleCIsIA0KICAgICAgICAgICAgICAgZ2d0aGVtZSA9IHRoZW1lX2J3KCksIA0KICAgICAgICAgICAgICAgcmVwZWw9VFJVRSwgIyBUcnkgdG8gYXZvaWQgb3ZlcmxhcHBpbmcgdGV4dA0KICAgICAgICAgICAgICAgbGFiZWxzaXplID0gMjAsDQogICAgICAgICAgICAgICBwb2ludHNpemUgPSA0LA0KICAgICAgICAgICAgICAgbWFpbiA9ICJLLW1lYW5zIGNsdXN0ZXJpbmcgb2YgZmlsdGVyZWQgV3lsZXIgcmVhZGNvdW50IGRhdGEiDQogICAgICAgICAgICAgICApICsNCg0KICAjIFNldCBzb21lIGdncGxvdCB0aGVtZSBpbmZvcm1hdGlvbg0KICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApKSArDQoNCiAgIyBTZXQgdGhlIGNvbG91ciBhbmQgZmlsbCBzY2hlbWUgdG8gdmlyaWRpcw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpICsNCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZCgpDQoNCiMgUHJpbnQgdGhlIHBsb3QhDQp3eWxlcl9rbWVhbnMucGxvdA0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMi4zLjMgSW50ZXJwcmV0aW5nIG91ciBSTkEtU2VxIGNsdXN0ZXJpbmcNCg0KTG9va2luZyBhdCB0aGUgcmVzdWx0LCB3aGF0J3MgbmljZSBhYm91dCB0aGlzIGtpbmQgb2Ygb3V0cHV0IGlzIHRoYXQgd2UgY2FuIGltbWVkaWF0ZWx5IHNlZSB0aGUgc2VwYXJhdGlvbiBvciBjbHVzdGVyaW5nIG9mIG91ciBkYXRhLiBJdCBsb29rcyBsaWtlIDQgd2FzIHRoZSB3YXkgdG8gZ28gYXMgdGhlcmUgYXJlIDQgdGlnaHQgZ3JvdXBpbmdzIGZyb20gb3VyIGRhdGEuIEl0IG1pZ2h0IGJlIHBvc3NpYmxlIHRvIGZ1cnRoZXIgc3ViZGl2aWRlIHRoZSBncm91cCBpbiB0aGUgdG9wIGxlZnQgb2Ygb3VyIGZpZ3VyZSBidXQgaXQncyB1bmxpa2VseS4NCg0KQmFzZWQgb24gb3VyIHNlbGVjdGlvbiBvZiBnZW5lcywgd2Ugb2JzZXJ2ZToNCg0KMS4gIDI0LSwgNDgtLCBhbmQgNzItaG91ciBzYW1wbGVzIG1vY2stdHJlYXRlZCAoRE1TTykgYW5kIG1vY2staW5mZWN0ZWQsIGNsdXN0ZXIgdG9nZXRoZXIgd2l0aCAyNC1ob3VyIG1vY2stdHJlYXRlZCBzYW1wbGVzIGluZmVjdGVkIHdpdGggU0FSUy1Db1YtMi4NCg0KMi4gIDQ4LSBhbmQgNzItaG91ciBzYW1wbGVzIG1vY2stdHJlYXRlZCAoRE1TTykgYW5kIGluZmVjdGVkIGJ5IFNBUlMtQ29WLTIgYXBwZWFyIHRvIHNoYXJlIGEgc2ltaWxhciBwcm9maWxlLg0KDQozLiAgMjQtaG91ciBzYW1wbGVzIHRyZWF0ZWQgd2l0aCAyMDAgbk0gMTdBQUcgKEhTUDkwIGluaGliaXRvcikgY2x1c3RlciB0b2dldGhlciB3aGV0aGVyIG9yIG5vdCB0aGV5IGFyZSBpbmZlY3RlZCB3aXRoIFNBUlMtQ29WLTIuDQoNCjQuICA0OC0gYW5kIDcyLWhvdXIgc2FtcGxlcyB0cmVhdGVkIHdpdGggMjAwIG5NIDE3QUFHIGNsdXN0ZXIgdG9nZXRoZXIgd2hldGhlciBvciBub3QgdGhleSBhcmUgaW5mZWN0ZWQgd2l0aCBTQVJTLUNvVi0yLg0KDQpUaGVzZSBncm91cGluZ3Mgc3VnZ2VzdCB0aGF0IHBlcmhhcHMgdGhlIDI0LWhvdXIgU0FSUy1Db1YtaW5mZWN0ZWQgdGltZXBvaW50IGlzIHNpbWlsYXIgdG8gdW5pbmZlY3RlZCBjb250cm9scy4gTWVhbndoaWxlIHRoZSA0OC0gYW5kIDcyLWhvdXIgU0FSUy1Db1YtaW5mZWN0ZWQgc2FtcGxlcyBhcmUgc2ltaWxhciBidXQgbGlrZWx5IHNob3dpbmcgdmVyeSBkaXN0aW5jdCB0cmFuc2NyaXB0aW9uYWwgY2hhbmdlcyBkdWUgdG8gaW5mZWN0aW9uLiBUcmVhdG1lbnQgd2l0aCB0aGUgSFNQOTAtaW5oaWJpdG9yLCBob3dldmVyLCBhcHBlYXJzIHRvIHN1ZmZpY2llbnRseSBhbHRlciBleHByZXNzaW9uIHByb2ZpbGVzIG9mIGluZmVjdGVkIGFuZCBtb2NrLWluZmVjdGVkIGNlbGxzIHRvIG1ha2VzIHRoZW0gY2x1c3RlciB0b2dldGhlci4gVGhpcyB3b3VsZCBiZSBhIGdvb2Qgc3RhcnRpbmcgcG9pbnQgdG8gYmVnaW4gZGlnZ2luZyBmdXJ0aGVyIGludG8gdGhlIGRhdGEuDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtd2FybmluZ30NCioqU2tlcHRpYyBvciBiZWxpZXZlcj8qKiBXaGlsZSBvdXIgYWJvdmUgYW5hbHlzaXMgc2VlbXMgdG8gbWFrZSBzZW5zZSB0byB1cywgaXQgbGFja3MgYSBkZWVwZXIgdW5kZXJzdGFuZGluZyBvZiB3aGF0IG1pZ2h0IGJlIGhhcHBlbmluZy4gRm9yIG9uZSB0aGluZywgd2UgaGF2ZW4ndCBldmVuIGxvb2tlZCBjbG9zZWx5IGF0IHRoZSBnZW5lcyB1c2VkIHRvIGNyZWF0ZSBvdXIgY2x1c3RlcmluZy4gSG93IG1hbnkgb2YgdGhlbSBhcmUgcmVsZXZhbnQgdG8gdGhlIGluZmVjdGlvbiBwcm9jZXNzPyBJcyB0aGUgY2x1c3RlcmluZyBkdWUgdG8gb2ZmLXRhcmdldCBlZmZlY3RzIG9mIHRoZSBIU1A5MCBpbmhpYml0b3IgKDE3QUFHKT8gV2hpbGUgY2x1c3RlcmluZyBpcyBhbiBlZmZlY3RpdmUgd2F5IHRvIHF1aWNrbHkgaWRlbnRpZnkgaWYgc3ViZ3JvdXBzIGV4aXN0IHdpdGhpbiB5b3VyIGRhdGEsIGl0J3MgaW1wb3J0YW50IHRvIGZvbGxvdyB1cCB0aGVzZSBmaW5kaW5ncyB3aXRoIGluLWRlcHRoIGFuYWx5c2VzIG9mIHRoZSBkYXRhLg0KOjo6DQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovc3F1aWRfUENBLnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KDQpZZXMgdGhlIGRhdGEgY2x1c3RlcnMsIGJ1dCB3aHkgZG9lcyBpdCBjbHVzdGVyPw0KOjo6DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDMuMC4wIERpbWVuc2lvbiByZWR1Y3Rpb24gdHJpbXMgdGhlIChkYXRhKSBmYXQNCg0KT25lIHByb2JsZW0gd2UgZW5jb3VudGVyZWQgaW4gb3VyIGFib3ZlIGFuYWx5c2lzIG9mIFJOQS1TZXEgZGF0YSBpcyB0aGF0IHRoZXJlIHdlcmUgc2ltcGx5IHRvbyBtYW55IGRpbWVuc2lvbnMhIFdpdGggXH4yN2sgZ2VuZSBlbnRyaWVzIGluIG91ciBkYXRhZnJhbWUsIHVzaW5nIGNsdXN0ZXJpbmcgdG8gYW5hbHlzZSB0aGUgZW50aXJlIGRhdGFzZXQgd291bGQgYmUgbWVtb3J5LWludGVuc2l2ZSBpZiBub3QgaW1wb3NzaWJsZSBhbHRvZ2V0aGVyLiBUbyBjaXJjdW12ZW50IHRoaXMgcHJvYmxlbSB3ZSBhdHRlbXB0ZWQgdG8gc3Vic2V0IG91ciBkYXRhIGluIHR3byB3YXlzIC0gZmlsdGVyaW5nIGJ5IHJlYWQgY291bnRzLCBhbmQgY29tcGFyaW5nIHZhcmlhbmNlIGFjcm9zcyBkYXRhc2V0cy4gVGhlc2UgYXBwcm9hY2hlcyB3ZXJlIGEgZm9ybSBvZiAqKipkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24qKiogLSBuYW1lbHkgZmVhdHVyZSBlbGltaW5hdGlvbi4gT3VyIGNob2ljZXMsIGhvd2V2ZXIsIG1heSBoYXZlIGluYWR2ZXJ0ZW50bHkgYmVlbiB0aHJvd2luZyBhd2F5IGRhdGEgdGhhdCBjb3VsZCBwcm92ZSBpbnNpZ2h0ZnVsISBUaGlzIGlzIHdoZXJlIG90aGVyIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiBtZXRob2RzIGNhbiBoZWxwIGd1aWRlIG91ciBhbmFseXNlcy4NCg0KWW91IGNhbiBhY2hpZXZlIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiBpbiB0aHJlZSBnZW5lcmFsIHdheXM6DQoNCjEuICAqKkZlYXR1cmUgZWxpbWluYXRpb24qKjogeW91IGNhbiByZWR1Y2UgdGhlIGZlYXR1cmUgc3BhY2UgYnkgcmVtb3ZpbmcgdW5oZWxwZnVsIGZlYXR1cmVzLiBObyBpbmZvcm1hdGlvbiBpcyBnYWluZWQgYnV0IHlvdSB0cmltIGRvd24geW91ciBkYXRhc2V0IHNpemUuDQoNCjIuICAqKkZlYXR1cmUgc2VsZWN0aW9uKio6IG9uIHRoZSByZXZlcnNlIHNpZGUgeW91IGNhbiBzaW1wbHkgY2hvb3NlIHRoZSBtb3N0IGltcG9ydGFudCBmZWF0dXJlcyB0byB5b3UuIEhvdyB3aWxsIHlvdSBkZWNpZGU/IFNvbWUgc2NoZW1lcyBpbmNsdWRlIHJhbmtpbmcgeW91ciBmZWF0dXJlcyBieSBpbXBvcnRhbmNlIGJ1dCB0aGlzIG1heSBzdWZmZXIgZnJvbSBpbmZvcm1hdGlvbiBsb3NzIGlmIGluY29ycmVjdCBmZWF0dXJlcyBhcmUgY2hvc2VuLg0KDQozLiAgKipGZWF0dXJlIGV4dHJhY3Rpb24qKjogY3JlYXRlIG5ldyBpbmRlcGVuZGVudCBmZWF0dXJlcyB3aGljaCBhcmUgYSBjb21iaW5hdGlvbiBvZiB0aGUgb2xkIGZlYXR1cmVzISBBdHRlbXB0IHRvIGV4dHJhY3QgdGhlIGVzc2VudGlhbCBmZWF0dXJlcyBvZiB5b3VyIGRhdGEgaW4gYSBtb3JlIGluZm9ybWF0aW9uYWx5IGRlbnNlIG1hbm5lci4NCg0KfCBUZWNobmlxdWUgICAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IFR5cGUgICAgICAgICAgICAgICAgICAgICAgICAgIHwgVXNlZnVsIGZvciAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgUmFuZG9tIGZvcmVzdCB8IFBvcHVsYXIgbWFjaGluZSBsZWFybmluZyBhbGdvcml0aG0gZm9yIGNsYXNzaWZpY2F0aW9uIHJhbmRvbWx5IGNob29zZXMgZnJvbSBzdWJzZXRzIG9mIGZlYXR1cmVzIHRvIGNsYXNzaWZ5IGRhdGEuIFRoZSBtb3N0IGZyZXF1ZW50bHkgYXBwZWFyaW5nIGZlYXR1cmVzIGFtb25nc3QgdGhlIGZvcmVzdHMgYXJlIGNob3Nlbi4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBGZWF0dXJlIHNlbGVjdGlvbiAgICAgICAgICAgICB8IE9ubHkgdGFrZXMgbnVtZXJpYyBpbnB1dHMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBQQ0EgICAgICAgICAgIHwgUHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcyBhdHRlbXB0cyB0byBtYXhpbWl6ZSB2YXJpYXRpb24gd2hlbiB0cmFuc2Zvcm1pbmcgdG8gYSBsb3dlci1kaW1lbnNpb25hbCBzcGFjZS4gQWxsIG5ldyBmZWF0dXJlcyBhcmUgaW5kZXBlbmRlbnQgb2Ygb25lIGFub3RoZXIuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IExpbmVhciBGZWF0dXJlIEV4dHJhY3Rpb24gICAgIHwgQWN0dWFsbHkgcmVhbGx5IGdvb2QgYXQgZmluZGluZyBvdXRsaWVycyBpbiBSTkFzZXEgZGF0YSB8DQp8IHQtU05FICAgICAgICAgfCB0LURpc3RyaWJ1dGVkIFN0b2NoYXN0aWMgTmVpZ2hib3VyIEVtYmVkZGluZyBpcyBhIG5vbi1saW5lYXIgdGVjaG5pcXVlIHNpbWlsYXIgdG8gUENBIHN1aXRhYmxlIGZvciBoaWdoLWRpbWVuc2lvbiBkYXRhc2V0cy4gSXQgYXR0ZW1wdHMgdG8gbWluaW1pemUgcHJvYmFiaWxpdGllcyBpbiBtaXJyb3JpbmcgbmVhcmVzdCBuZWlnaGJvdXIgcmVsYXRpb25zaGlwcyBpbiB0cmFuc2Zvcm1pbmcgZnJvbSBoaWdoIHRvIGxvdy1kaW1lbnNpb24gc3BhY2VzIHwgTm9uLWxpbmVhciBGZWF0dXJlIGV4dHJhY3Rpb24gfCBEZXRlcm1pbmUgaWYgeW91ciBkYXRhIGhhcyB1bmRlcmx5aW5nIHN0cnVjdHVyZSAgICAgICAgIHwNCnwgVU1BUCAgICAgICAgICB8IFVuaWZvcm0gbWFuaWZvbGQgYXBwcm94aW1hdGlvbiBhbmQgcHJvamVjdGlvbiBpcyBwcm9qZWN0aW9uLWJhc2VkIGxpa2UgdC1TTkUsIHRoaXMgdGVjaG5pcXVlIGF0dGVtcHRzIHRvIHByZXNlcnZlIGxvY2FsIGRhdGEgc3RydWN0dXJlIGJ1dCBoYXMgaW1wcm92ZWQgdHJhbnNsYXRpb24gb2YgZ2xvYmFsIGRhdGEgc3RydWN0dXJlLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBOb24tbGluZWFyIGZlYXR1cmUgZXh0cmFjdGlvbiB8IEZhc3RlciB0aGFuIHQtU05FICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KDQpTaW5jZSB3ZSdyZSBub3QgZXhhY3RseSBidWlsZGluZyBhIGNsYXNzaWZpZXIgYnV0IHJhdGhlciB0cnlpbmcgdG8gZmluZCB0cmVuZHMgaW4gb3VyIGRhdGEsIHdlIHdvbid0IGJlIGxvb2tpbmcgYXQgUmFuZG9tIEZvcmVzdHMgaGVyZS4gSGVyZSB3ZSBhcmUgaW50ZXJlc3RlZCBpbiAqZXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcyouIFdlIGhhdmUgYSBkYXRhIHNldCB3ZSB3YW50IHRvIHVuZGVyc3RhbmQsIHNvbWV0aW1lcyBpdCBpcyB0b28gY29tcGxleCB0byBqdXN0IHByb2plY3Qgb3IgZGl2aW5lIDEpIHRoZSB1bmRlcmx5aW5nIHN0cnVjdHVyZSBhbmQgMikgdGhlIGZlYXR1cmVzIHRoYXQgZHJpdmUgdGhhdCBzdHJ1Y3R1cmUuDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtc3VjY2Vzc30NCioqVGhlcmUncyBtb3JlIHRvIGV4cGxvcmUgd2l0aCBkaW1lbnNpb24gcmVkdWN0aW9uOioqIFRoZSBhYm92ZSB0YWJsZSBpcyBqdXN0IGEgc21hbGwgc3Vic2V0IG9mIHBvdGVudGlhbGx5IGRpZmZlcmVudCBraW5kcyBvZiBkaW1lbnNpb24gcmVkdWN0aW9uIG1ldGhvZHMuIFBDQSBmb3IgaW5zdGFuY2UgaGFzIDIgInZhcmlhbnRzIjogTXVsdGlwbGUgQ29ycmVzcG9uZGVuY2UgQW5hbHlzaXMgKE1DQSkgd2hpY2ggZGVhbHMgd2l0aCByZWxhdGlvbnNoaXBzIGJldHdlZW4gY2F0ZWdvcmljYWwgdmFyaWFibGVzIHJhdGhlciB0aGFuIGNvbnRpbnVvdXMgb25lcywgYW5kIEluZGVwZW5kZW50IENvbXBvbmVudCBBbmFseXNpcyAoSUNBKSB3aGljaCBhbHNvIHVzZXMgbGluZWFyIGRpbWVuc2lvbiByZWR1Y3Rpb24gdG8gaWRlbnRpZnkgaW5kZXBlbmRlbnQgY29tcG9uZW50cyBpbiB5b3VyIGRhdGFzZXQuIFlvdSBjYW4gY2hlY2sgb3V0IGEgbGl0dGxlIG1vcmUgW2hlcmVdKGh0dHBzOi8vd3d3LnNwaWNld29ya3MuY29tL3RlY2gvYXJ0aWZpY2lhbC1pbnRlbGxpZ2VuY2UvYXJ0aWNsZXMvd2hhdC1pcy1kaW1lbnNpb25hbGl0eS1yZWR1Y3Rpb24vKSBhbmQgW292ZXIgaGVyZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRGltZW5zaW9uYWxpdHlfcmVkdWN0aW9uKQ0KOjo6DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAzLjEuMCBSZWR1Y2UgeW91ciBkaW1lbnNpb25hbGl0eSB3aXRoIFBDQQ0KDQpHb2luZyBiYWNrIHRvIG91ciBQSFUgZGF0YSwgd2UgdXNlZCA3IGRpbWVuc2lvbnMgdG8gY2xhc3NpZnkgb3VyIGRhdGEgYnV0IHdoYXQgaWYgd2Ugd2FudGVkIHRvIHRyYW5zZm9ybSB0aGF0IGluZm9ybWF0aW9uIGluIHNvbWUgd2F5IHRvIHJlZHVjZSB0aGUgbnVtYmVyIG9mIGRpbWVuc2lvbnMgbmVlZGVkIHRvIHJlcHJlc2VudCB0aGVpciByZWxhdGlvbnNoaXBzPyBGb3Igb3VyIGRhdGEgc2V0LCA3IGRpbWVuc2lvbnMgaXNuJ3QgYSBsb3Qgb2YgZmVhdHVyZXMgYnV0IGluIG90aGVyIGNhc2VzIChsaWtlIFJOQS1TZXEpIHlvdSBtaWdodCBlbmNvdW50ZXIgZmVhdHVyZS1kZW5zZSBkYXRhc2V0cyBhbmQgd2l0aG91dCBrbm93aW5nICphIHByaW9yaSogd2hpY2ggb25lcyBhcmUgbWVhbmluZ2Z1bCwgUENBIHByb3ZpZGVzIGEgcGF0aCBmb3J3YXJkIGluIGNsYXNzaWZ5aW5nIG91ciBkYXRhLg0KDQpOb3cgdGhlcmUgYXJlIGNhdmVhdHMgdG8gUENBLiBJdCBwcm9kdWNlcyBsaW5lYXIgZmVhdHVyZSBleHRyYWN0aW9uIGJ5IGJ1aWxkaW5nIG5ldyBmZWF0dXJlcyBpbiB5b3VyIG4tZGltZW5zaW9uYWwgc3BhY2Ugc3VjaCB0aGF0IGVhY2ggbmV3IGZlYXR1cmUgKGtpbmQgb2YgbGlrZSBhIHByb2plY3RlZCBsaW5lKSBtYXhpbWl6ZXMgdmFyaWFuY2UgdG8gdGhlIG9yaWdpbmFsIGRhdGEgcG9pbnRzLiBFYWNoIG5ldyBjb21wb25lbnQgbXVzdCBiZSB1bmNvcnJlbGF0ZWQgd2l0aCAoaWUgcGVycGVuZGljdWxhciB0bykgdGhlIHByZXZpb3VzIG9uZXMgd2hpbGUgYWNjb3VudGluZyBmb3IgdGhlIG5leHQgaGlnaGVzdCBhbW91bnQgb2YgdmFyaWFuY2UuIE1vcmUgcmVzb3VyY2VzIG9uIGhvdyB0aGlzIHdvcmtzIGluIHRoZSByZWZlcmVuY2VzLg0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L1BDQV9zZWNvbmRfcHJpbmNpcGFsLmdpZj9yYXc9dHJ1ZSIgd2lkdGg9IjkwMCIvPg0KDQpIb3cgZG8gd2UgZ28gYWJvdXQgbWF4aW1pemluZyB0aGUgdmFyaWFuY2UgKHNwcmVhZCBvZiByZWQgZG90cykgdG8gb3VyIGRhdGEgZmVhdHVyZXM/IEZpbmRpbmcgdGhlIHBvaW50IHdoZXJlIHZhcmlhbmNlIGlzIG1heGltaXplZCwgYWxzbyBtaW5pbWl6ZXMgZXJyb3IgKHJlZCBsaW5lIGxlbmd0aHMpLiBHZW5lcmF0ZWQgYnkgdXNlciBgQW1vZWJhYCBvbiBbc3RhY2tleGNoYW5nZS5jb21dKGh0dHBzOi8vc3RhdHMuc3RhY2tleGNoYW5nZS5jb20vcXVlc3Rpb25zLzI2OTEvbWFraW5nLXNlbnNlLW9mLXByaW5jaXBhbC1jb21wb25lbnQtYW5hbHlzaXMtZWlnZW52ZWN0b3JzLWVpZ2VudmFsdWVzKQ0KOjo6DQoNCkFsbCBtYXRoIGFzaWRlLCBvdXIgZ29hbCBpcyB0byByZWR1Y2Ugb3VyIGZlYXR1cmUgc2V0IHRvIHNvbWV0aGluZyBzbWFsbGVyIGJ5IHRyeWluZyB0byByZXByZXNlbnQgb3VyIGRhdGEgd2l0aCB0aGVzZSBuZXcgZmVhdHVyZXMuIEp1c3QgcmVtZW1iZXIgdGhhdCBoaWdobHkgdmFyaWFibGUgZGF0YSBhbmQgb3V0bGllcnMgY2FuIGRvbWluYXRlIHlvdXIgcHJpbmNpcGFsIGNvbXBvbmVudHMuDQoNCkZvciBzaW1wbGljaXR5IGxldCdzIGhlYWQgYmFjayBhbmQgbG9vayBhdCBvdXIgUEhVIGFnZSBncm91cCBkYXRhIGFnYWluLiBUbyBpbGx1c3RyYXRlIG91ciBleGFtcGxlIHdpdGggUENBLCBsZXQncyB1c2UgdGhlIG9yaWdpbmFsIGRhdGEgbm9ybWFsaXplZCBieSBQSFUgcG9wdWxhdGlvbi4NCg0KYGBge3J9DQojIFZpZXcgdGhlIG5vcm1hbGl6ZWQgUEhVIGFnZSBncm91cCBkYXRhDQpoZWFkKGNvdmlkX2RlbW9ncmFwaGljc19ub3JtLm14KQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAzLjIuMCBVc2UgYFBDQSgpYCB0byBnZW5lcmF0ZSBvdXIgYW5hbHlzaXMNCg0KVG8gZ2VuZXJhdGUgb3VyIGFuYWx5c2lzIG9mIHRoZSBQSFUgZGF0YSwgd2UnbGwgdXNlIHRoZSBgRmFjdG9NaW5lUmAgZnVuY3Rpb24gYFBDQSgpYCBmb3Igd2hpY2ggdGhlcmUgYXJlIHNvbWUgcGFyYW1ldGVycyB3ZSdsbCBiZSB1c2luZyB0aGF0IHdlIHNob3VsZCBkaXNjdXNzOiAtIGBYYCBhIGRhdGEgZnJhbWUgb2YgKm4qIHJvd3MgKG9ic2VydmF0aW9ucykgYW5kICpwKiBjb2x1bW5zIChudW1lcmljIHZhcmlhYmxlcykgLSBgbmNwYCB0aGUgbnVtYmVyIG9mIGRpbWVuc2lvbnMga2VwdCBpbiB0aGUgcmVzdWx0cyAoNSBpcyB0aGUgZGVmYXVsdCkgLSBgc2NhbGUudW5pdGAgYSBib29sZWFuIHRvIHNjYWxlIHlvdXIgZGF0YSAoVFJVRSBieSBkZWZhdWx0KQ0KDQojIyMgMy4yLjEgV2hhdCBkb2VzIGl0IG1lYW4gdG8gc2NhbGUgb3VyIGRhdGE/DQoNClJlbWVtYmVyIHRoYXQgUENBIGlzIHRyeWluZyB0byBtYXhpbWl6ZSBkaXN0YW5jZSBiZXR3ZWVuIGEgcHJpbmNpcGxlIGNvbXBvbmVudCBhbmQgYWxsIG9mIHRoZSBvYnNlcnZhdGlvbnMgc3VwcGxpZWQuIERlcGVuZGluZyBvbiB0aGUgbmF0dXJlIG9mIHlvdXIgdmFyaWFibGVzIHlvdSBtYXkgaGF2ZSwgZm9yIGluc3RhbmNlLCB0d28gZGlmZmVyZW50IHVuaXQgdHlwZXMgbGlrZSBoZWlnaHQgYW5kIG1hc3MuIFNtYWxsZXIgY2hhbmdlcyBpbiBoZWlnaHQgbWF5IGJlIG1hdGNoZWQgd2l0aCBtdWNoIGxhcmdlciBjaGFuZ2VzIGluIG1hc3Mgb3IganVzdCB3aWRlciBvdmVyYWxsIHZhcmlhbmNlLiBUaGlzIG1heSBsZWFkIHRoZSBQQ0EgYWxnb3JpdGhtIHRvIHByaW9yaXRpemUgbWFzcyBvdmVyIGhlaWdodCB3aGVuIHlvdSdkIHByZWZlciB0aGV5IGhhdmUgYW4gZXF1YWwgaW1wb3J0YW5jZS4gQnkgY2VudGVyaW5nIHlvdXIgbWVhbiBhbmQgc2NhbGluZyBkYXRhIHRvIHVuaXQgdmFyaWFuY2UsIGV2ZXJ5dGhpbmcgaXMgY29tcGFyZWQgYXMgYSAqKnotc2NvcmUqKiwgYnJpbmdpbmcgdGhlIG92ZXJhbGwgdmFyaWFuY2UgYWNyb3NzIGEgdmFyaWFibGUgdG8gd2l0aGluIFx+MyBzdGFuZGFyZCBkZXZpYXRpb25zLg0KDQpMZXQncyBjb21wYXJlIFBDQSB3aXRoIGFuZCB3aXRob3V0IHNjYWxpbmcgc2hhbGwgd2U/DQoNCmBgYHtyfQ0KIyBCdWlsZCBhIFBDQSBvZiBvdXIgUEhVIGRhdGEgd2l0aCBzY2FsaW5nIGFwcGxpZWQNCnBodV9zY2FsZWQucGNhIDwtIFBDQSguLi4sIA0KICAgICAgICAgICAgICAgICAgICAgIHNjYWxlLnVuaXQgPSAuLi4sICMgV2hhdCBoYXBwZW5zIHdoZW4gd2UgZG9uJ3Qgc2NhbGUgdGhlIGRhdGE/DQogICAgICAgICAgICAgICAgICAgICAgbmNwID0gLi4uLA0KICAgICAgICAgICAgICAgICAgICAgIGdyYXBoID0gVFJVRSkNCg0KIyBCdWlsZCBhIFBDQSBvZiBvdXIgUEhVIGRhdGEgV0lUSE9VVCBzY2FsaW5nIGFwcGxpZWQNCnBodV91bnNjYWxlZC5wY2EgPC0gUENBKGNvdmlkX2RlbW9ncmFwaGljc19ub3JtLm14WywtMV0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGUudW5pdCA9IC4uLiwNCiAgICAgICAgICAgICAgICAgICAgICAgIG5jcCA9IDcsDQogICAgICAgICAgICAgICAgICAgICAgICBncmFwaCA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQojIFRha2UgYSBsb29rIGF0IHRoZSBpbmZvcm1hdGlvbiBpbnNpZGUgb3VyIFBDQSBvYmplY3QNCnByaW50KC4uLikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuMi4yIE91ciBQQ0Egb2JqZWN0IGhhcyBhIGNvbXBsZXggbnVtYmVyIG9mIGRhdGEgcGllY2VzDQoNClJlZ2FyZGxlc3Mgb2Ygc2NhbGluZywgdGhlIHJlc3VsdCBvZiBvdXIgYFBDQSgpYCBjYWxsIHByb2R1Y2VzIGFuIG9iamVjdCB3aXRoIG1hbnkgdmFyaWFibGVzIHdlIGNhbiBhY2Nlc3MuIEFib3ZlIHlvdSBjYW4gc2VlIGEgYnJpZWYgZGVzY3JpcHRpb24gZm9yIGVhY2ggdmFyaWFibGUgYnV0IHdlIGFyZSBtb3N0IGludGVyZXN0ZWQgaW4gYSBmZXcgcGFydGljdWxhciBvbmVzOg0KDQotICAgYGVpZ2AgaG9sZHMgb3VyIGRpbWVuc2lvbmFsIGRhdGEgYnV0IGFsc28gZGVzY3JpYmVzIGp1c3QgaG93IG11Y2ggZWFjaCBuZXcgcHJpbmNpcGxlIGNvbXBvbmVudCBkZXNjcmliZXMgdGhlIG92ZXJhbGwgdmFyaWF0aW9uIG9mIG91ciBkYXRhLg0KDQotICAgYHZhcmAgaG9sZHMgdGhlIHJlc3VsdHMgb2YgYWxsIHRoZSB2YXJpYWJsZXMuIFdlIGNhbiB1c2UgdGhlc2UgdG8gZ3JhcGggYW5kIHZpc3VhbGl6ZSBvdXIgZGF0YS4NCg0KLSAgIGBpbmQkY29vcmRgIHdpbGwgYWxsb3cgdXMgdG8gcGxvdCB0aGUgY29vcmRpbmF0ZXMgb2Ygb3VyIG9ic2VydmF0aW9ucyBhbG9uZyB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudHMuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAzLjMuMCBVc2UgdGhlIGVpZ2VudmFsdWVzIHRvIGRldGVybWluZSB0aGUgcGVyY2VudCB2YXJpYW5jZSBvZiBlYWNoIGNvbXBvbmVudA0KDQpUaGUgZWlnZW52YWx1ZXMgZnJvbSBvdXIgYW5hbHlzaXMgcGFpciB3aXRoIHRoZSBlaWdlbnZlY3RvcnMgKHByaW5jaXBsZSBjb21wb25lbnRzKSB0byBoZWxwIHRyYW5zZm9ybSBvdXIgZGF0YSBmcm9tIHRoZSBvcmlnaW5hbCBmZWF0dXJlIHNldCB0byB0aGUgbmV3IHNldCBvZiBmZWF0dXJlcy4gV2hpbGUgdGhlIGVpZ2VudmVjdG9ycyBtYXkgZGV0ZXJtaW5lIHRoZSBkaXJlY3Rpb25zIG9mIHRoZSBuZXcgZmVhdHVyZSBzcGFjZSwgdGhlICplaWdlbnZhbHVlKiByZXByZXNlbnRzIHRoZSAqKm1hZ25pdHVkZSoqIG9mIHRoZSB2ZWN0b3IgYW5kIGluIHRoaXMgY2FzZSBjYW4gYmUgdXNlZCB0byBjYWxjdWxhdGUgdGhlIHBlcmNlbnQgb2Ygb3ZlcmFsbCB2YXJpYW5jZSBleHBsYWluZWQgYnkgb3VyIGVpZ2VudmVjdG9yLg0KDQpUaGUgaW1wb3J0YW50IHRha2UtYXdheSBpcyB0aGF0IHdlIGNhbiBub3cgc2VlIGp1c3QgaG93IG11Y2ggb2Ygb3VyIHZhcmlhbmNlIGlzIGV4cGxhaW5lZCBpbiBlYWNoIG5ldyBwcmluY2lwbGUgY29tcG9uZW50LiBXZSBjYW4gYWNjZXNzIHRoaXMgaW5mb3JtYXRpb24gZGlyZWN0bHkgZnJvbSBgcGh1X3NjYWxlZC5wY2FgIG9yIGJ5IHVzaW5nIHRoZSBmdW5jdGlvbiBgZ2V0X2VpZ2VudmFsdWUoKWAuIFdlIGNhbiBhbHNvIHBsb3QgdGhpcyBhcyBhIGJhcmNoYXJ0IGluc3RlYWQgdXNpbmcgYGZ2aXpfZWlnKClgLg0KDQpgYGB7cn0NCiMgTG9vayBhdCBvdXIgZWlnZW52YWx1ZXMgZGlyZWN0bHkNCnBodV9zY2FsZWQucGNhJC4uLg0KDQojIFVzZSBnZXRfZWlnZW52YWx1ZSgpIHRvIGxvb2sgYXQgb3VyIGVpZ2VudmFsdWVzDQpnZXRfZWlnZW52YWx1ZShwaHVfc2NhbGVkLnBjYSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuMy4xIEJ1aWxkIGEgc2NyZWUgcGxvdCB0byBsb29rIGF0IHRoZSBlZmZlY3Qgb2YgeW91ciBkaW1lbnNpb25zDQoNClNlZSBob3cgbmVhcmx5IDgwJSBvZiBvdXIgdmFyaWF0aW9uIGlzIGV4cGxhaW5lZCBpbiBqdXN0IG91ciBmaXJzdCBkaW1lbnNpb24/IExldCdzIHVzZSBgZnZpel9laWcoKWAgdG8gZGlzcGxheSB0aGlzIGluZm9ybWF0aW9uIHZpc3VhbGx5IGluIHdoYXQgaXMga25vd24gYXMgYSBzY3JlZSBwbG90LiBJdCdzIGVzc2VudGlhbGx5IGEgYmFycGxvdC9saW5lcGxvdCBjb21ibyBidXQgd2hhdCB3ZSdyZSBpbnRlcmVzdGVkIGluIGlzIGZvbGxvd2luZyB0aGUgbGluZXMgbXVjaCBsaWtlIG91ciBjbHVzdGVyLWVzdGltYXRpbmcgV1NTIG1ldGhvZC4gV2UgdXNlIGEgInNoYXJwIGVsYm93IiB0byBkZXRlcm1pbmUgaG93IG1hbnkgcHJpbmNpcGFsIGNvbXBvbmVudHMgd2UgbmVlZC4NCg0KYGBge3J9DQoNCiMgVmlzdWFsaXplIHRoZSBpbXBhY3Qgb2Ygb3VyIGVpZ2VudmFsdWVzDQpmdml6X2VpZyguLi4sIGFkZGxhYmVscyA9IFRSVUUpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuMy4yIE1vc3Qgb2Ygb3VyIHZhcmlhbmNlIGlzIGFjY291bnRlZCBmb3IgaW4gdGhlIGZpcnN0IHR3byBwcmluY2lwbGUgY29tcG9uZW50cyENCg0KTG9va2luZyBhdCBvdXIgZWlnZW52YWx1ZXMgd2UgY2FuIHNlZSB0aGF0IGV2ZW4gdGhvdWdoIDcgbmV3IFBDcyB3ZXJlIGdlbmVyYXRlZCwgdGhlIGZpcnN0IHR3byBleHBsYWluIGFsbW9zdCA5MiUgb2Ygb3VyIHZhcmlhbmNlLiBUaGF0IHN1Z2dlc3RzIHRoYXQgd2hhdGV2ZXIgKmxpbmVhciogc2VwYXJhdGlvbiBpbiBvdXIgZGF0YSBleGlzdHMsIHdlIGNhbiByZWNyZWF0ZSBpbiBhIHR3by1kaW1lbnNpb25hbCBwcm9qZWN0aW9uIG9mIGNvb3JkaW5hdGVzIGZyb20gUEMxIGFuZCBQQzIuIFRoaXMgc3VnZ2VzdHMgdGhhdCB3ZSBjYW4gcmVjcmVhdGUgdGhlIHVuZGVybHlpbmcgc3RydWN0dXJlIG9mIG91ciBkYXRhIHdpdGggdGhlc2UgdHdvIG5ldyBmZWF0dXJlcyBpbnN0ZWFkIG9mIHVzaW5nIGEgNy1kaW1lbnNpb25hbCBzcGFjZSENCg0KVGhpcyBtYWtlcyBzb21lIHNlbnNlIHdoZW4gd2UgdGFrZSBhIGNsb3NlciBsb29rIGF0IHRoZSBkYXRhLiBGb3IgaW5zdGFuY2UsIHRoZXJlIGlzbid0IG11Y2ggdmlzdWFsIGluZm9ybWF0aW9uIGZvdW5kIGluIG91ciBgMCB0byA0YCBhZ2UgcmFuZ2UuIFRoZSBzbWFsbCBkaXN0cmlidXRpb24gb2YgaW5mb3JtYXRpb24gaW4gdGhlc2UgZmVhdHVyZXMgbWF5IG5vdCBkbyBtdWNoLCBpbiB0aGUgZ3JhbmQgc2NoZW1lLCB0byBjaGFuZ2UgaG93IHRoZSBQSFVzIGFyZSBzZXBhcmF0ZWQgZnJvbSBlYWNoIG90aGVyLiBUaGVuIGFnYWluLCBwZXJoYXBzIHRoZXkgKmRvKiBjb250YWluIGluZm9ybWF0aW9uIHRoYXQgd2Ugc2ltcGx5IGNhbm5vdCBzZWUgZWFzaWx5ISBIb3cgd2lsbCB3ZSBrbm93Pw0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMy40LjAgSW52ZXN0aWdhdGUgeW91ciB2YXJpYWJsZXMgd2l0aCBgZ2V0X3BjYV92YXIoKWANCg0KV2l0aGluIG91ciBQQ0EsIHdlIGNhbiBhY2Nlc3MgaW5mb3JtYXRpb24gcmVnYXJkaW5nIGhvdyBvdXIgb3JpZ2luYWwgdmFyaWFibGVzIGFyZSB0cmFuc2Zvcm1lZCBpbnRvIHRoZSBuZXcgc3BhY2UuIFRoaXMgaXMgYWxsIHN0b3JlZCBpbiB0aGUgYHZhcmAgZWxlbWVudCBvZiBvdXIgUENBIG9iamVjdC4gV2UgY2FuIGV4dHJhY3QgdGhlIGFzcGVjdHMgb2YgdGhpcyB1c2luZyB0aGUgYGdldF9wY2FfdmFyKClgIGFuZCB2aXN1YWxpemUgdGhlc2UgdG8gZGV0ZXJtaW5lIHRoZSBxdWFsaXR5IG9mIG91ciB2YXJpYWJsZXMgYW5kIHRoZWlyIHJlcHJlc2VudGF0aW9uIGJ5IHRoZSBuZXcgcHJpbmNpcGFsIGNvbXBvbmVudHMuDQoNCmBgYHtyfQ0KIyBXaGF0IGlzIHRoZSBpbmZvcm1hdGlvbiBhc3NvY2lhdGVkIHdpdGggb3VyIG9yaWdpbmFsIHZhcmlhYmxlcw0KcGh1LnZhciA8LSBnZXRfcGNhX3ZhciguLi4pDQoNCnBodS52YXINCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuNC4xIFdoYXQgaXMgdGhlIGNvbnRyaWJ1dGlvbiBvZiBlYWNoIHZhcmlhYmxlIHRvIHRoZSBuZXcgY29tcG9uZW50cz8NCg0KRWFjaCBvcmlnaW5hbCB2YXJpYWJsZSBtYXksIHRvIHNvbWUgZGVncmVlLCBpbmZsdWVuY2UgdGhlIGFtb3VudCBvZiBpbmZvcm1hdGlvbiBjYXB0dXJlZCBpbnRvIGVhY2ggbmV3IHByaW5jaXBhbCBjb21wb25lbnQuIFdoZW4gd2UgbG9vayBhdCBlYWNoIHdlIGNhbiBzZWUgdGhlIGJyZWFrZG93biBvZiB2YXJpYWJsZXMsIHdoaWNoIGluIHNvbWUgY2FzZXMsIGNhbiByZXZlYWwgdG8gdXMgd2hlcmUgbW9yZSBvciBsZXNzIHZhcmlhdGlvbiBpcyBmb3VuZCBhcyB3ZWxsLg0KDQpgYGB7cn0NCiMgV2hhdCBpcyB0aGUgY29udHJpYnV0aW9uIG9mIGVhY2ggdmFyaWFibGU/DQpwaHUudmFyJC4uLg0KYGBgDQoNCkZyb20gYWJvdmUgd2UgY2FuIHNlZSB0aGF0IG91ciBvcmlnaW5hbCB2YXJpYWJsZXMgZXF1YWxseSBjb250cmlidXRlIHRvIG91ciBmaXJzdCBwcmluY2lwYWwgY29tcG9uZW50IGJ1dCB0aGVyZSBpcyBtdWNoIG1vcmUgY29udHJpYnV0aW9uIGluIFBDMiBmcm9tIHRocmVlIHZhcmlhYmxlczogMCB0byA0LCA1IHRvIDExIGFuZCA4MCsuIEZyb20gb3VyIHByZXZpb3VzIHNjcmVlIHBsb3QgdGhhdCBtZWFucyB0aGF0IDEyLjglIG9mIG92ZXJhbGwgdmFyaWF0aW9uLCB3aGljaCBpcyBkZXNjcmliZWQgaW4gUEMyLCBpcyBtYWlubHkgZnJvbSB0aGVzZSB0aHJlZSBncm91cHMuDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtc3VjY2Vzc30NCioqSG93IG1hbnkgZGltZW5zaW9ucyBzaG91bGQgSSBnZXQgYW5kIGhvdyBtYW55IGRvIEkga2VlcD8qKiBJdCBpcyBhdCB0aGlzIHBvaW50IHRoYXQgd2Ugc2hvdWxkIGRpc2N1c3MgdGhlICptYXhpbXVtKiBudW1iZXIgb2YgcG9zc2libGUgZGltZW5zaW9ucy4gR2l2ZW4gKioqbioqKiBvYnNlcnZhdGlvbnMsIGFuZCAqKipwKioqIGZlYXR1cmVzLCB0aGUgbWF4aW11bSBudW1iZXIgb2YgZGltZW5zaW9ucyBhZnRlciByZWR1Y3Rpb24gaXMgKioqbWluKG4tMSwgcCkqKiouIEluIHRlcm1zIG9mIGhvdyBtYW55IGRpbWVuc2lvbnMgeW91IGtlZXAsIHlvdSBzaG91bGQgY29uc2lkZXIgdGhlIGdlbmVyYWwgcnVsZSBvZiB0aHVtYiB0aGF0ICoqKmF0IGxlYXN0IDgwJSoqKiBvZiB5b3VyIHZhcmlhdGlvbiBzaG91bGQgYmUgYWNjb3VudGVkIGZvciBieSB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudHMgdGhhdCB5b3UgY2hvb3NlLiBLZWVwIHRoYXQgaW4gbWluZCENCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuNC4yIENoZWNraW5nIHRoZSBxdWFsaXR5IGFuZCByZXByZXNlbnRhdGlvbiBvZiB5b3VyIHZhcmlhYmxlcyBpbiB0aGUgUENBIHdpdGggYGNvb3JkYCBhbmQgYGNvcmANCg0KRnJvbSB0aGUgcmVtYWluaW5nIHZhcmlhYmxlIGluZm9ybWF0aW9uLCBgY29vcmRgIGFuZCBgY29yYCBjYW4gYm90aCBiZSB1c2VkIHRvIHBsb3Qgb3VyIG9yaWdpbmFsIHZhcmlhYmxlcyBhbG9uZyBkaWZmZXJlbnQgcGFpcnMgb2YgcHJpbmNpcGFsIGNvbXBvbmVudHMuIFRoZXNlIHZhbHVlcyBhcmUgdGhlIHNhbWUgYmVjYXVzZSB0aGlzIGlzIG5vdCBhICpwcm9qZWN0aW9uKiBvZiBvdXIgdmFyaWFibGVzIG9udG8gdGhlIFBDcyBidXQgYSBjb3JyZWxhdGlvbiBvZiB0aGVtISBUaGUgZGlzdGFuY2UgYmV0d2VlbiB0aGUgb3JpZ2luIGFuZCB0aGUgdmFyaWFibGUgY29vcmRpbmF0ZXMgZ2F1Z2VzIHRoZSBxdWFsaXR5IG9mIHRoZSB2YXJpYWJsZXMgb24gdGhlIG1hcC4gVGhpcyBudW1iZXIgaXMgc3VtbWFyaXplZCBpbiB0aGUgYGNvczJgIChzcXVhcmVkIGNvc2luZSkgZWxlbWVudC4NCg0KYGBge3J9DQojIExvb2sgYXQgdGhlIGNvb3JkaW5hdGVzIG9mIG91ciB2YXJpYWJsZXMgYWNyb3NzIHRoZSBQQ3MuDQpwaHUudmFyJC4uLg0KcGh1LnZhciQuLi4NCmBgYA0KDQpgYGB7cn0NCiMgV2hhdCBpcyB0aGUgcXVhbGl0eSBvZiBvdXIgdmFyaWFibGVzPw0KIyBUaGUgaGlnaGVyIHRoZSB2YWx1ZSB0aGUgYmV0dGVyIQ0KcGh1LnZhciQuLi4NCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuNC4zIFBsb3QgeW91ciB2YXJpYWJsZSBpbmZvcm1hdGlvbiBvbiBhIHVuaXQgY2lyY2xlIHdpdGggYGZ2aXpfcGNhX3ZhcigpYA0KDQpUbyBjYXB0dXJlIGFsbCBvZiBvdXIgaW5mb3JtYXRpb24gaW4gYSBzaW5nbGUgdmlzdWFsaXphdGlvbiB3ZSBjYW4gdXNlIGBmdml6X3BjYV92YXIoKWAgdG8gYXNzZXNzIG91ciB2YXJpYWJsZXMuIFRoZSBgZnZpel9wY2FfdmFyKClgIGZ1bmN0aW9uIHdpbGwgcGxvdCBjb3MyIHZhbHVlcyBvbiBhIDItZGltZW5zaW9uIGF4aXMgb2YgeW91ciBjaG9vc2luZy4gSGlnaCBxdWFsaXR5IHZhcmlhYmxlcyByZXByZXNlbnRlZCBieSB0aGUgdHdvIGRpbWVuc2lvbnMgb2YgeW91ciBjaXJjbGUgKGllIFBDMSB2cyBQQzIpIHdpbGwgYmUgY2xvc2VyIHRvIHRoZSBjaXJjdW1mZXJlbmNlIG9mIHRoZSBjaXJjbGUuDQoNClNvbWUgdmFyaWFibGVzIG1heSByZXF1aXJlIG1vcmUgdGhhbiAyIGNvbXBvbmVudHMgdG8gcmVwcmVzZW50IHRoZSBkYXRhIGFuZCB0aGV5IHdpbGwgdGhlcmVmb3JlIGZhbGwgaW5zaWRlIHRoZSBjaXJjbGUuIExvdyBxdWFsaXR5IGNvcnJlbGF0aW9ucyB3aWxsIGJlIGNsb3NlciB0byB0aGUgb3JpZ2luIHNpbmNlIHRoZXkgY29ycmVsYXRlIGxlc3Mgd2l0aCB0aGUgdHdvIGRpbWVuc2lvbnMgcmVwcmVzZW50ZWQgb24gdGhlIGdyYXBoLg0KDQpBcyB3ZSB3aWxsIHZlcmlmeSBmcm9tIGFib3ZlLCBtb3N0IG9mIG91ciB2YXJpYWJsZXMgYXJlIHBvc2l0aXZlbHkgY29ycmVsYXRlZCB3aXRoIFBDMS4gQWNyb3NzIGJvdGggUEMxL1BDMiwgb3VyIHZhcmlhYmxlcyBjb3JyZWxhdGUgaGlnaGx5Lg0KDQpUd28gaW1wb3J0YW50IHBhcmFtZXRlcnMgdG8ga2VlcCBpbiBtaW5kOg0KDQotICAgYFhgOiB0aGUgUENBIG9iamVjdCB0aGF0IHlvdSB3YW50IHRvIGNyZWF0ZS4NCg0KLSAgIGBheGVzYDogYSBudW1lcmljIHZlY3RvciB3aXRoIHRoZSB0d28gZGltZW5zaW9ucyB5b3Ugd2FudCB0byBleGFtaW5lLg0KDQpgYGB7cn0NCiMgQ29tcGFyZSBob3cgb3VyIHZhcmlhYmxlcyBjb250cmlidXRlIGFuZCBjb3JyZWxhdGUgd2l0aCBQQzEvUEMyDQpmdml6X3BjYV92YXIoWCA9IHBodV9zY2FsZWQucGNhLCANCiAgICAgICAgICAgICBjb2wudmFyID0gLi4uLCAjIEhvdyB3aWxsIHdlIGNvbG91ciBvdXIgZGF0YS9saW5lcw0KICAgICAgICAgICAgIGdyYWRpZW50LmNvbHMgPSBjKCJncmVlbiIsICJ5ZWxsb3ciLCAicmVkIiksIA0KICAgICAgICAgICAgIGxhYmVsc2l6ZSA9IDYsDQogICAgICAgICAgICAgcmVwZWwgPSBUUlVFLCAjIG1ha2Ugc3VyZSB0ZXh0IGRvZXNuJ3Qgb3ZlcmxhcA0KICAgICAgICAgICAgIGF4ZXMgPSBjKDEsMikgIyBEZXRlcm1pbmUgd2hpY2ggUENzIHlvdSB3YW50IHRvIGdyYXBoDQogICAgICAgICAgICApICsgDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkNCmBgYA0KDQpgYGB7cn0NCiMgQ29tcGFyZSBob3cgb3VyIHZhcmlhYmxlcyBjb250cmlidXRlIGFuZCBjb3JyZWxhdGUgd2l0aCBQQzIvUEMzDQpmdml6X3BjYV92YXIocGh1X3NjYWxlZC5wY2EsIA0KICAgICAgICAgICAgIGNvbC52YXIgPSAiY29udHJpYiIsICMgSG93IHdpbGwgd2UgY29sb3VyIG91ciBkYXRhL2xpbmVzDQogICAgICAgICAgICAgZ3JhZGllbnQuY29scyA9IGMoImdyZWVuIiwgInllbGxvdyIsICJyZWQiKSwgDQogICAgICAgICAgICAgbGFiZWxzaXplID0gNiwNCiAgICAgICAgICAgICByZXBlbCA9IFRSVUUsICMgbWFrZSBzdXJlIHRleHQgZG9lc24ndCBvdmVybGFwDQogICAgICAgICAgICAgYXhlcyA9IGMoLi4uKSAjIERldGVybWluZSB3aGljaCBQQ3MgeW91IHdhbnQgdG8gZ3JhcGgNCiAgICAgICAgICAgICkgKyANCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTApKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAzLjUuMCBDaGVjayBpZiBQSFVzIGNsdXN0ZXIgb3Igc2VwYXJhdGUgd2l0aCBgZnZpel9wY2FfaW5kKClgDQoNCk5vdyB0aGF0IHdlJ3ZlIGdlbmVyYXRlZCBvdXIgUENBLCBsZXQncyBzZWUgaWYgdGhlcmUgaXMgYW55IGNsZWFyIHNlcGFyYXRpb24gYmV0d2VlbiBvdXIgUEhVcyBhY3Jvc3MgdGhlIGZpcnN0IHR3byBkaW1lbnNpb25zIG9mIG5ldyBmZWF0dXJlcy4gTXVjaCBsaWtlIG91ciB2YXJpYWJsZXMsIGFsbCBvZiB0aGUgaW5kaXZpZHVhbCBjb29yZGluYXRlIGRhdGEgY2FuIGJlIGZvdW5kIGluIG91ciBgaW5kYCBlbGVtZW50IG9mIG91ciBQQ0Egb2JqZWN0LiBXaXRoaW4gdGhlcmUsIHdlJ2xsIGZpbmQgdGhlIGBjb29yZGAgaW5mb3JtYXRpb24gc28gd2UgY291bGQgZGlyZWN0bHkgYnVpbGQgYSBnZ3Bsb3Qgd2l0aCBgcGh1X3NjYWxlZC5wY2EkaW5kJGNvb3JkYCBCVVQgdGhlcmUncyBhIHNpbXBsZXIgd2F5Lg0KDQpXZSdsbCBwYXJzZSB0aHJvdWdoIHRoZSBQQ0Egb2JqZWN0IHdpdGggYGZ2aXpfcGNhX2luZCgpYCB0byBwbG90IG91ciBkYXRhIGFsb25nIHRoZSBmaXJzdCB0d28gcHJpbmNpcGxlIGNvb3JkaW5hdGVzLiBXZSBjYW4gZGVmaW5lIGBwb2ludHNpemVgIGFuZCBgY29sLmluZGAgdG8gaGVscCBhZGQgc29tZSBwb3B1bGF0aW9uIHNpemUgaW5mb3JtYXRpb24gdG8gb3VyIFBIVXMuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9MTJ9DQoNCiMgR3JhcGggb3VyIHNjYWxlZCBQQ0EgZGF0YS4NCnRlc3QgPC0NCmZ2aXpfcGNhX2luZCguLi4sIA0KICAgICAgICAgICAgIHBvaW50c2l6ZSA9IGNvdmlkX2RlbW9ncmFwaGljc19ub3JtLm14WywxXSwgIyBTZXQgcG9pbnQgc2l6ZSBhbmQgY29sb3VyIGJ5IFBIVSBwb3B1bGF0aW9uDQogICAgICAgICAgICAgY29sLmluZCA9IGxvZzEwKGNvdmlkX2RlbW9ncmFwaGljc19ub3JtLm14WywxXSksDQogICAgICAgICAgICAgcmVwZWwgPSBUUlVFLCAjIGF2b2lkIG92ZXJsYXBwaW5nIHRleHQgcG9pbnRzDQogICAgICAgICAgICAgbGFiZWxzaXplID0gNSwgDQogICAgICAgICAgICAgYXhlcyA9IGMoMSwyKQ0KICAgICAgICAgICAgKSArIA0KICAgIA0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpICsgIyBNYWtlIG91ciB0ZXh0IGxhcmdlcg0KDQogICAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMywgMTApKSArICMgVXBkYXRlIHRoZSBwb2ludCBzaXplIHJhbmdlDQoNCiAgICBzY2FsZV9jb2xvdXJfdmlyaWRpc19jKCkgIyBDaGFuZ2UgdGhlIGNvbG91ciBzY2hlbWUNCg0KdGVzdA0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9MTJ9DQojIEdyYXBoIG91ciBVTnNjYWxlZCBQQ0EgZGF0YS4NCg0KZnZpel9wY2FfaW5kKC4uLiwgDQogICAgICAgICAgICAgcG9pbnRzaXplID0gY292aWRfZGVtb2dyYXBoaWNzX25vcm0ubXhbLDFdLCAjIFNldCBwb2ludCBzaXplIGFuZCBjb2xvdXIgYnkgUEhVIHBvcHVsYXRpb24NCiAgICAgICAgICAgICBjb2wuaW5kID0gbG9nMTAoY292aWRfZGVtb2dyYXBoaWNzX25vcm0ubXhbLDFdKSwNCiAgICAgICAgICAgICByZXBlbCA9IFRSVUUsICMgYXZvaWQgb3ZlcmxhcHBpbmcgdGV4dCBwb2ludHMNCiAgICAgICAgICAgICBsYWJlbHNpemUgPSA1LCANCiAgICAgICAgICAgICBheGVzID0gYygxLDIpDQogICAgICAgICAgICApICsgDQogICAgDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkgKyAjIE1ha2Ugb3VyIHRleHQgbGFyZ2VyDQoNCiAgICBzY2FsZV9zaXplKHJhbmdlID0gYygzLCAxMCkpICsgIyBVcGRhdGUgdGhlIHBvaW50IHNpemUgcmFuZ2UNCg0KICAgIHNjYWxlX2NvbG91cl92aXJpZGlzX2MoKSAjIENoYW5nZSB0aGUgY29sb3VyIHNjaGVtZQ0KYGBgDQoNCmBgYHtyfQ0KIyBXaGF0IGFyZSB0aGUgY29udHJpYnV0aW9ucyBvZiBlYWNoIGFnZSBncm91cCB0byB0aGUgdmFyaW91cyB1bnNjYWxlZCBkaW1lbnNpb25zDQpwaHVfdW5zY2FsZWQucGNhJHZhciRjb250cmliDQpgYGANCg0KYGBge3J9DQojIExvb2sgYXQgb3VyIHVuc2NhbGVkIGVpZ2VudmFsdWVzIGRpcmVjdGx5DQpwaHVfdW5zY2FsZWQucGNhJGVpZw0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy41LjEgU2NhbGVkIHZzIHVuc2NhbGVkIGRhdGENCg0KQXMgeW91IGNhbiBzZWUgZnJvbSBvdXIgZ3JhcGhpbmcgb2YgYm90aCBQQ0Egc2V0cywgd2hlbiB3ZSBjaG9vc2Ugbm90IHRvIHNjYWxlIG91ciBkYXRhIHNvbWV0aGluZyBhIGxpdHRsZSBtb3JlIGRyYXN0aWMgaGFwcGVucy4gUEMxJ3MgcG9ydGlvbiBvZiB2YXJpYW5jZSBpbmNyZWFzZXMgdG8gYSA4My4yJSBhY2NvdW50aW5nIG9mIG92ZXJhbGwgdmFyaWFuY2UuIFdlIHNlZSBtYW55IG9mIG91ciBwb2ludHMgbW92ZSBhbmQgc2VwYXJhdGlvbiBiZXR3ZWVuIHNvbWUgb2Ygb3VyIFBIVXMgaXMgaW5jcmVhc2VkIChLaW5nc3Rvbi9Gcm9udGVuYWMgYW5kIFBlZWwpLiBUaGVzZSBjaGFuZ2VzIGFyZSBsaWtlbHkgZHVlIHRvIHRoZSBsYXJnZXIgcmFuZ2Ugb2YgdmFsdWVzIGZyb20gdGhlIDIwIHRvIDM5IGFnZSBncm91cCB3aGljaCBub3cgY29udHJpYnV0ZXMgdG8gMzElIG9mIFBDMSdzIHZhcmlhdGlvbi4NCg0KU28gdGhpbmsgY2FyZWZ1bGx5IGFib3V0IHlvdXIgZmVhdHVyZXMgYW5kIHdoYXQgdGhleSByZXByZXNlbnQuIERlcGVuZGluZyBvbiB0aGVpciByYW5nZSwgYW5kIHVuaXQgdmFsdWVzLCB5b3UgbWF5IGJlIGluYWR2ZXJ0ZW50bHkgd2VpZ2hpbmcgdGhlbSBtb3JlIHRoYW4geW91ciBvdGhlciBmZWF0dXJlcyEgU2NhbGluZyBhZGp1c3RzIHlvdXIgZGF0YSBzbyB0aGF0IGFsbCBmZWF0dXJlcyBhcmUgd2VpZ2h0ZWQgZXF1YWxseSENCg0KIyMgMy42LjAgQ2FuIHdlIGNsdXN0ZXIgb3VyIHRyYW5zZm9ybWVkIGRhdGE/DQoNCk5vdyB0aGF0IHdlJ3ZlIGVmZmVjdGl2ZWx5IHJlZHVjZWQgdGhlIGNvbXBsZXhpdHkgb2Ygb3VyIGRhdGEsIGNhbiB3ZSBwcm9kdWNlIHRoZSBzYW1lIGstbWVhbnMgY2x1c3RlcmluZyBhcyBiZWZvcmU/IExldCdzIHRyeSB0byBjbHVzdGVyIGp1c3Qgb24gdGhlIHR3byBkaW1lbnNpb25zIHByZXNlbnRlZCwgd2hpY2ggd2UgaGF2ZSBhbHJlYWR5IGdyYXBoZWQhDQoNCmBgYHtyfQ0KIyBIb3cgbWFueSBjbHVzdGVycyBzaG91bGQgd2UgdXNlIGJhc2VkIG9uIG91ciBQQ0EgZGF0YT8NCmZ2aXpfbmJjbHVzdChwaHVfc2NhbGVkLnBjYSRpbmQkY29vcmRbLDE6Ml0sIEZVTmNsdXN0ZXIgPSBrbWVhbnMsIG1ldGhvZD0id3NzIikNCmZ2aXpfbmJjbHVzdChwaHVfc2NhbGVkLnBjYSRpbmQkY29vcmRbLDE6Ml0sIEZVTmNsdXN0ZXIgPSBrbWVhbnMsIG1ldGhvZD0ic2lsaG91ZXR0ZSIpDQpgYGANCg0KYGBge3J9DQojIEdlbmVyYXRlIG91ciBrLW1lYW5zIGFuYWx5c2lzIGFuZCB2aXN1YWxpemUgaXQNCg0Kc2V0LnNlZWQoMTIzKQ0KDQpwaHVfUENBX2ttZWFucy5wbG90IDwtDQoNCiAgICBrbWVhbnMoLi4uLCBjZW50ZXJzID0gMykgJT4lIA0KDQogICAgZnZpel9jbHVzdGVyKC4sICMgb3VyIGstbWVhbnMgb2JqZWN0DQogICAgICAgICAgICAgICAgIGRhdGEgPSAuLi4sICMgT3VyIG9yaWdpbmFsIGRhdGEgbmVlZGVkIGZvciBQQ0EgdG8gdmlzdWFsaXplDQogICAgICAgICAgICAgICAgIGVsbGlwc2UudHlwZSA9ICJjb252ZXgiLCANCiAgICAgICAgICAgICAgICAgZ2d0aGVtZSA9IHRoZW1lX2J3KCksIA0KICAgICAgICAgICAgICAgICByZXBlbD1UUlVFLCAjIFRyeSB0byBhdm9pZCBvdmVybGFwcGluZyB0ZXh0DQogICAgICAgICAgICAgICAgIGxhYmVsc2l6ZSA9IDIwLA0KICAgICAgICAgICAgICAgICBwb2ludHNpemUgPSA0LA0KICAgICAgICAgICAgICAgICBtYWluID0gIkstbWVhbnMgY2x1c3RlcmluZyBvZiBQSFUgYnkgbm9ybWFsaXplZCBjYXNlIG51bWJlciBBRlRFUiBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzIg0KICAgICAgICAgICAgICAgICApICsNCg0KICAgICMgU2V0IHNvbWUgZ2dwbG90IHRoZW1lIGluZm9ybWF0aW9uDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSkgKw0KDQogICAgIyBTZXQgdGhlIGNvbG91ciBhbmQgZmlsbCBzY2hlbWUgdG8gdmlyaWRpcw0KICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19kKCkgKw0KICAgIHNjYWxlX2NvbG91cl92aXJpZGlzX2QoKQ0KDQojIHBodV9wY2Ffa21lYW5zLnBsb3QNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTE2LCBmaWcuaGVpZ2h0PTEyfQ0KIyBQbG90IGJvdGggb2Ygb3VyIGZpZ3VyZXMgdG9nZXRoZXINCmZpZy5zaG93PSJob2xkIjsgb3V0LndpZHRoPSI1MCUiOyBvdXQuaGVpZ2h0ID0gIjUwJSINCnBodV9rbWVhbnMucGxvdA0KcGh1X1BDQV9rbWVhbnMucGxvdA0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy42LjEgV2FzIGl0IHdvcnRoIGl0IHRvIHVzZSBQQ0E/DQoNCkZyb20gb3VyIGVzdGltYXRpb25zLCB0aGUgY2x1c3RlcmluZyBjaG9pY2VzIGxvb2sgbmVhciBpZGVudGljYWwgYW5kIHRoaXMgaXMgbm90IGV4YWN0bHkgdGhlIGJlc3QgZGF0YXNldCB0byB3b3JrIHdpdGggc2luY2UgaXQgaXMgcmF0aGVyIHNtYWxsIGJ1dCB5b3UgY2FuIHNlZSB0aGF0IHdlIGhhdmUgbm93IHRyYW5zZm9ybWVkIG91ciBkYXRhIGludG8ganVzdCAyIGZlYXR1cmVzISBDb3VsZCB3ZSBkbyB0aGUgc2FtZSB1c2luZyBqdXN0IDIgZmVhdHVyZXMgZnJvbSBvdXIgb3JpZ2luYWwgZGF0YXNldD8gTm8hIFNvIGltYWdpbmUgdHJhbnNmb3JtaW5nIGEgbXVjaCBsYXJnZXIgYW5kIGZlYXR1cmUtaGVhdnkgZGF0YXNldCBpbnRvIGEgc21hbGxlciBudW1iZXIgb2YgZmVhdHVyZXMuIEZyb20gdGhlIGRhdGEsIHdlIGFyZSBhbHNvIGFibGUgdG8gZGlzY2VybiB3aGljaCBvZiBvdXIgb3JpZ2luYWwgZmVhdHVyZXMgbWF5IHJlYWxseSBjb250cmlidXRlIHRvIGVhY2ggbmV3IHByaW5jaXBhbCBjb21wb25lbnQhDQoNClNvbWV0aGluZyB0byBjb25zaWRlciB0aGUgbmV4dCB0aW1lIHlvdSdyZSB3b3JraW5nIHdpdGggYSBsYXJnZSBkYXRhIHNldCENCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDMuNy4wIFBDQSBvbiBhIGxhcmdlIFJOQS1TZXEgZGF0YXNldA0KDQpOb3cgdGhhdCB3ZSd2ZSB3YWxrZWQgdGhyb3VnaCBob3cgdG8gZ2VuZXJhdGUgYSBQQ0EgZm9yIGEgc2ltcGxlciBkYXRhc2V0LCBsZXQncyByZXR1cm4gdG8gb3VyIFJOQS1TZXEgcmVhZGNvdW50IGRhdGEuIFdlIHdvbid0IHRyaW0gZG93biB0aGUgZGF0YSBhdCBhbGwgYnV0IHJhdGhlciBqdXN0IHByb3ZpZGUgdGhlIGVudGlyZSBkYXRhc2V0IGFzIGEgbWF0cml4IGZvciBmZWF0dXJlIHJlZHVjdGlvbi4gTGV0J3MgcmV2aWV3IHRoZSBzdGVwczoNCg0KMS4gIEdlbmVyYXRlIGEgbWF0cml4IG9mIHlvdXIgZGF0YSwgbmFtaW5nIHRoZSBjb2x1bW5zIGFuZCByb3dzIGJ5IHdoYXRldmVyIGluZm9ybWF0aW9uIHlvdSBtaWdodCBoYXZlLiBNYWtlIHN1cmUgeW91ciBjb2x1bW5zIHJlcHJlc2VudCB5b3VyIGZlYXR1cmVzLCBhbmQgcm93cyBhcmUgb2JzZXJ2YXRpb25zLg0KMi4gIENyZWF0ZSB0aGUgUENBIG9iamVjdC4NCjMuICBSZXZpZXcgeW91ciBlaWdlbnZhbHVlcyBhbmQgZWlnZW52ZWN0b3JzIHRvIGFzc2VzcyBob3cgd2VsbCB5b3VyIHJlZHVjdGlvbiB3b3JrZWQuIERldGVybWluZSBob3cgbWFueSBkaW1lbnNpb25zIHlvdSB3aXNoIHRvIHVzZSBpbiBmdXJ0aGVyIGFuYWx5c2lzLg0KNC4gIENhbGN1bGF0ZSB0aGUgbnVtYmVyIG9mIHBvc3NpYmxlIGNsdXN0ZXJzLg0KNS4gIFBsb3QgdGhlIHJlc3VsdHMuDQoNCmBgYHtyfQ0KIyAxLiBHZW5lcmF0ZSBvdXIgbWF0cml4IGZyb20gdGhlIHJlYWRjb3VudCBkYXRhDQp3eWxlcl9yZWFkY291bnRzX2FsbC5teCA8LQ0KDQogIHd5bGVyX3JlYWRjb3VudHMuZGYgJT4lIA0KICAjIFJlbmFtZSB0aGUgY29sdW1ucyBieSByZW1vdmluZyB0aGUgZmlyc3QgcG9ydGlvbjogQUVDSUlfeHgNCiAgcmVuYW1lX3dpdGgoLiwgfiBzdHJfcmVwbGFjZShzdHJpbmcgPSAueCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9IHIiKFx3Kl9cZCpfKSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2UgPSAiIikpICU+JSANCg0KICAjIEtlZXAgb25seSB0aGUgZXhwZXJpbWVudGFsIG9ic2VydmF0aW9ucyAoaG93IG1hbnkgYXJlIHRoZXJlPykNCiAgZHBseXI6OnNlbGVjdChjKDM6MzgpKSAlPiUgDQoNCiAgIyBDb252ZXJ0IHRvIGEgbWF0cml4DQogIGFzLm1hdHJpeCgpICU+JSANCg0KICAjIFRyYW5zcG9zZSB0aGUgbWF0cml4IHNvIG91ciBjb2x1bW5zIGFyZSBub3cgZ2VuZXMNCiAgLi4uDQoNCiMgTmFtZSB0aGUgcm93cyBvZiB0aGUgbWF0cml4DQpjb2xuYW1lcyh3eWxlcl9yZWFkY291bnRzX2FsbC5teCkgPC0gd3lsZXJfcmVhZGNvdW50cy5kZiRnZW5lDQoNCiMgV2UnbGwgcGVlayBhdCB0aGUgc3RydWN0dXJlIG9mIHRoZSBtYXRyaXguIExvb2tpbmcgYXQgaXQgd2l0aCBoZWFkKCkgd2lsbCBiZSBtZXNzeS4NCnN0cih3eWxlcl9yZWFkY291bnRzX2FsbC5teCkNCmBgYA0KDQpgYGB7cn0NCiMgMi4gR2VuZXJhdGUgdGhlIFBDQSBvYmplY3QNCnJlYWRjb3VudHNfc2NhbGVkLnBjYSA8LSBQQ0EoLi4uLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGUudW5pdCA9IFRSVUUsICMgV2hhdCBoYXBwZW5zIHdoZW4gd2UgZG9uJ3Qgc2NhbGUgdGhlIGRhdGE/DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5jcCA9IDQwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmFwaCA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQojIDMuIFJldmlldyBob3cgbXVjaCB2YXJpYW5jZSBpcyBleHBsYWluZWQgYnkgdGhlIG5ldyBjb21wb25lbnRzDQpyZWFkY291bnRzX3NjYWxlZC5wY2EkZWlnDQpgYGANCg0KYGBge3J9DQojIDQuIEhvdyBtYW55IGNsdXN0ZXJzIHNob3VsZCB3ZSB1c2UgYmFzZWQgb24gb3VyIFBDQSBkYXRhPw0KZnZpel9uYmNsdXN0KHJlYWRjb3VudHNfc2NhbGVkLnBjYSRpbmQkY29vcmRbLDE6Ml0sIEZVTmNsdXN0ZXIgPSBrbWVhbnMsIG1ldGhvZD0ic2lsaG91ZXR0ZSIpDQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD0xNiwgZmlnLmhlaWdodD0xMn0NCiMgNS4gR2VuZXJhdGUgb3VyIGstbWVhbnMgYW5hbHlzaXMgYW5kIHZpc3VhbGl6ZSBpdA0Kc2V0LnNlZWQoMTIzKQ0KDQpyZWFkY291bnRzX1BDQV9rbWVhbnMucGxvdCA8LQ0KDQogICMgTGV0J3MgZ28gd2l0aCA0IGNsdXN0ZXJzIGluIG9yZGVyIHRvIGNvbXBhcmUgdG8gb3VyIG9yaWdpbmFsIHZlcnNpb24uDQogICMgVGhpcyBpcyBjbG9zZSB0byB0aGUgc2lsaG91ZXR0ZSB2YWx1ZSBhcyB3ZWxsLg0KICBrbWVhbnMocmVhZGNvdW50c19zY2FsZWQucGNhJGluZCRjb29yZFssMTo2XSwgY2VudGVycyA9IC4uLiwgKSAlPiUgDQoNCiAgZnZpel9jbHVzdGVyKC4sICMgb3VyIGstbWVhbnMgb2JqZWN0DQogICAgICAgICAgICAgICBkYXRhID0gcmVhZGNvdW50c19zY2FsZWQucGNhJGluZCRjb29yZFssMToyXSwgIyBPdXIgb3JpZ2luYWwgZGF0YSBuZWVkZWQgZm9yIFBDQSB0byB2aXN1YWxpemUNCiAgICAgICAgICAgICAgIGVsbGlwc2UudHlwZSA9ICJjb252ZXgiLCANCiAgICAgICAgICAgICAgIGdndGhlbWUgPSB0aGVtZV9idygpLCANCiAgICAgICAgICAgICAgIHJlcGVsPVRSVUUsICMgVHJ5IHRvIGF2b2lkIG92ZXJsYXBwaW5nIHRleHQNCiAgICAgICAgICAgICAgIGxhYmVsc2l6ZSA9IDIwLA0KICAgICAgICAgICAgICAgcG9pbnRzaXplID0gNCwNCiAgICAgICAgICAgICAgIG1haW4gPSAiSy1tZWFucyBjbHVzdGVyaW5nIG9mIFBIVSBieSBub3JtYWxpemVkIGNhc2UgbnVtYmVyIEFGVEVSIFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMiDQogICAgICAgICAgICAgICApICsNCg0KICAjIFNldCBzb21lIGdncGxvdCB0aGVtZSBpbmZvcm1hdGlvbg0KICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTApKSArDQoNCiAgIyBTZXQgdGhlIGNvbG91ciBhbmQgZmlsbCBzY2hlbWUgdG8gdmlyaWRpcw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpICsNCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZCgpDQoNCiMgTG9vayBhdCBvdXIgb3JpZ2luYWwgcGxvdCB3aXRoIDQgY2VudHJlcw0Kd3lsZXJfa21lYW5zLnBsb3QNCg0KIyBWZXJzdXMgb3VyIFBDQSB3aXRoIDQgY2VudHJlcw0KcmVhZGNvdW50c19QQ0Ffa21lYW5zLnBsb3QNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KSWYgd2Ugd2VyZSB0byBjb21wYXJlIG91ciB0d28gdmVyc2lvbnMgb2YgdGhlIGstbWVhbnMgY2x1c3RlcmluZyB1c2luZyByYXcgZGF0YSB2cyBvdXIgUENBIGRpbWVuc2lvbi1yZWR1Y2VkIGRhdGEsIHRoZSByZXN1bHRzIGFyZSBwcmV0dHkgc2ltaWxhciBhbHRob3VnaCBub3QgcXVpdGUuIEkndmUgYWxzbyBhZGRlZCB3aGF0IHlvdXIgZGF0YSB3b3VsZCBsb29rIGxpa2UgaWYgeW91IGRpZCBOT1Qgc2NhbGUgdGhlIGRhdGEgYmVmb3JlIHBlcmZvcm1pbmcgUENBLg0KDQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBBc3BlY3QgICAgICAgICAgICAgICAgICAgIHwgUmF3IEstbWVhbnMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgUENBIEstbWVhbnMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgUENBIEstbWVhbnMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgUENBIEstbWVhbnMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCis6PT09PT09PT09PT09PT09PT09PT09PT09PTorOj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PTorOj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PTorOj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PTorOj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PTorDQp8IFNjYWxlZCAgICAgICAgICAgICAgICAgICAgfCBTY2FsZSBhdCB0aW1lIG9mIGstbWVhbnMgICAgICAgICAgICAgICAgICAgfCBTY2FsZSBhdCB0aW1lIG9mIFBDQSAgICAgICAgICAgICAgICAgICAgfCBTY2FsZSBhdCB0aW1lIG9mIFBDQSAgICAgICAgICAgICAgICAgICAgICAgfCBVbnNjYWxlZCBhdCB0aW1lIG9mIFBDQSAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgRmVhdHVyZXMgdXNlZCBmb3Igay1tZWFucyB8IDI3SyBnZW5lcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IFRvcCAyIGNvbXBvbmVudHMgKDQzJSB2YXIpICAgICAgICAgICAgICB8IFRvcCA2IGNvbXBvbmVudHMgKDY3JSB2YXIpICAgICAgICAgICAgICAgICB8IFRvcCAyIGNvbXBvbmVudHMgKDczICUgdmFyKSAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBEaW0gMSAgICAgICAgICAgICAgICAgICAgIHwgNDQuNiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMjYuMSUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMjYuMSUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNTAuMyUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IERpbSAyICAgICAgICAgICAgICAgICAgICAgfCAxNy44JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAxNi45JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAxNi45JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAyMi43JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgQ2x1c3RlciAxICAgICAgICAgICAgICAgICB8IDI0aDogYWxsLCBETVNPLXRyZWF0ZWQ7ICAgICAgICAgICAgICAgICAgICB8ICoqMjRoKzQ4aDogU0FSUy1Db1YyIGluZmVjdGlvbiwgRE1TTzsqKiB8IDI0aDogYWxsLCBETVNPLXRyZWF0ZWQ7ICAgICAgICAgICAgICAgICAgICB8IDI0aDogYWxsLCBETVNPLXRyZWF0ZWQ7ICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNDhoKzcyaDogdW5pbmZlY3RlZCwgRE1TTyAgICAgICAgICAgICAgICAgIHwgKipTb21lIDI0aCs0OGg6IHVuaW5mZWN0ZWQsIERNU08qKiAgICAgIHwgNDhoKzcyaDogdW5pbmZlY3RlZCwgRE1TTyAgICAgICAgICAgICAgICAgIHwgNDhoKzcyaDogdW5pbmZlY3RlZCwgRE1TTyAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IENsdXN0ZXIgMiAgICAgICAgICAgICAgICAgfCA0OGgrNzJoOiBTQVJTLUNvVjIgaW5mZWN0aW9uLCBETVNPLXRyZWF0ZWQgfCAqKjQ4aDogdW5pbmZlY3RlZCwgRE1TTzsqKiAgICAgICAgICAgICAgfCA0OGgrNzJoOiBTQVJTLUNvVjIgaW5mZWN0aW9uLCBETVNPLXRyZWF0ZWQgfCA0OGgrNzJoOiBTQVJTLUNvVjIgaW5mZWN0aW9uLCBETVNPLXRyZWF0ZWQgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICoqNzJoOiBhbGwsIERNU08tdHJlYXRlZCoqICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBDbHVzdGVyIDMgICAgICAgICAgICAgICAgIHwgMjRoOiAxN0FBRy10cmVhdGVkICAgICAgICAgICAgICAgICAgICAgICAgIHwgMjRoOiAxN0FBRy10cmVhdGVkICAgICAgICAgICAgICAgICAgICAgIHwgMjRoOiAxN0FBRy10cmVhdGVkICAgICAgICAgICAgICAgICAgICAgICAgIHwgMjRoOiAxN0FBRy10cmVhdGVkICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IENsdXN0ZXIgNCAgICAgICAgICAgICAgICAgfCA0OGgrNzJoOiAxN0FBRy10cmVhdGVkICAgICAgICAgICAgICAgICAgICAgfCA0OGgrNzJoOiAxN0FBRy10cmVhdGVkICAgICAgICAgICAgICAgICAgfCA0OGgrNzJoOiAxN0FBRy10cmVhdGVkICAgICAgICAgICAgICAgICAgICAgfCA0OGgrNzJoOiAxN0FBRy10cmVhdGVkICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCg0KU29tZSBpbnRlcmVzdGluZyBjb25jbHVzaW9ucyB3ZSBjYW4gZHJhdyBmcm9tIHRoaXMgYXJlDQoNCjEuICBXaGV0aGVyIG91ciBkYXRhIGlzIHNjYWxlZCBqdXN0IHByaW9yIHRvIHBlcmZvcm1pbmcgay1tZWFucywgc2NhbGVkIG9yIHVuc2NhbGVkIHdpdGggUENBLCB3ZSBzZWUgdGhhdCBvdXIgMTdBQUctdHJlYXRlZCBzYW1wbGVzIGhhdmUgMiBtYWluIGNsdXN0ZXJzIHJlZ2FyZGxlc3Mgb2YgaW5mZWN0aW9uIHN0YXR1cy4gRWl0aGVyIDI0aCBvciA0OGgrNzJoIGRhdGEuIFRoZXJlIGlzIGxpa2VseSBhIHN0cm9uZyBlZmZlY3Qgb24gZXhwcmVzc2lvbiBieSB0aGUgKioqMTdBQUcgdHJlYXRtZW50KioqIHRoYXQgaXMgbXVjaCBzdHJvbmdlciB0aGFuIHRoZSBpbmZlY3Rpb24gYnkgU0FSUy1Db1YyLiBJbiBmYWN0LCBpZiB3ZSB3ZXJlIHRvIGV4cGxvcmUgdGhpcyBmdXJ0aGVyIHdlIHdvdWxkIHNlZSB0aGF0IGlmIHdlIGNyZWF0ZWQgbW9yZSBjbHVzdGVycywgd2Ugd291bGQgZmluZCB0aGF0ICoqKnRpbWUqKiogcGxheXMgYSBiaWdnZXIgcm9sZSBpbiBmdXJ0aGVyIHNlcGFyYXRpbmcgdGhlc2UgZ3JvdXBzIHRoYW4gU0FSUy1Db1YyIGluZmVjdGlvbi4NCjIuICBDaG9vc2luZyB0aGUgbnVtYmVyIG9mIGZlYXR1cmVzIGRvZXMgbWF0dGVyIGV2ZW4gYWZ0ZXIgUENBISBMb29raW5nIGF0IGp1c3Qgb3VyIHNjYWxlZCBQQ0EgZGF0YSB0byBjcmVhdGUgYSBrLW1lYW4gY2x1c3RlcmluZywgd2UgY2FuJ3QgZ2V0IGdvb2QgY29uY29yZGFuY2Ugd2l0aCBvdXIgcmF3IGRhdGEgYW5hbHlzaXMgdXNpbmcgb25seSBvdXIgKioqdG9wIHR3byBQQ0EgZmVhdHVyZXMqKiogd2hpY2ggYWNjb3VudCBmb3IganVzdCAqKio0MyUgb2YgdmFyaWFuY2UqKiouIE9uIHRoZSBvdGhlciBoYW5kLCBpZiB3ZSBjaG9vc2Ugb3VyICoqKnRvcCBzaXggZmVhdHVyZXMqKiogZm9yIGstbWVhbnMgY2x1c3RlcmluZyB3aGljaCBhY2NvdW50IGZvciAqKio2NyUgb2YgdmFyaWFuY2UqKiosIHdlIGRvIGdldCB0aGUgc2FtZSBjbHVzdGVycyByZXR1cm5lZCwgYWxiZWl0IGEgc2xpZ2hsdHkgZGlmZmVyZW50IGxvb2tpbmcgcHJvamVjdC4NCg0KVGhlcmVmb3JlIFBDQSBjYW4gaGVscCBpZGVudGlmeSBmYWN0b3JzIHdoaWNoIGhlYXZpbHkgaW5mbHVlbmNlIHlvdXIgc2FtcGxlcyBhcyBsb25nIGFzIHlvdSBhbHNvIGNvcnJlY3RseSBjaG9vc2UgZW5vdWdoIGZlYXR1cmVzIHRvIHJlcHJlc2VudCB5b3VyIHRyYW5zZm9ybWVkIGRhdGEuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAzLjguMCBSZXN0YXJ0IFIhDQoNCkF0IHRoaXMgcG9pbnQsIHdlJ3ZlIGFjY3VtdWxhdGVkIGEgbG90IG9mIGRhdGEgaW4gbWVtb3J5LiBXZSB3b24ndCBuZWVkIG91ciBwcmV2aW91cyBkYXRhIGFueW1vcmUgc28gbGV0J3MgcmVzdGFydCBSIGFuZCByZWxvYWQgdGhlIGxpYnJhcmllcyB3ZSdsbCBuZWVkLiBZb3UgY2FuDQoNCmBgYHtyfQ0KIyBQYWNrYWdlcyB0byBoZWxwIHRpZHkgb3VyIGRhdGENCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KDQojIFBhY2thZ2VzIGZvciB0aGUgZ3JhcGhpY2FsIGFuYWx5c2lzIHNlY3Rpb24NCmxpYnJhcnkodmlyaWRpcykNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KDQojIERhdGEgcHJvamVjdGlvbiBwYWNrYWdlcw0KbGlicmFyeShSdHNuZSkNCmxpYnJhcnkodW1hcCkNCg0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDQuMC4wIE5vbi1saW5lYXIgcHJvamVjdGlvbg0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L3QtU05FX1VNQVBfZXhhbXBsZXMucG5nP3Jhdz10cnVlIiB3aWR0aD0iOTAwIi8+DQoNCkhvdyBkbyB3ZSBpZGVudGlmeSB0cmVuZHMgb3IgZ3JvdXBzIHdpdGhpbiBkZWVwbHkgY29tcGxleCBkYXRhIGluIGFuIHVuc3VwZXJ2aXNlZCBtYW5uZXI/DQo6OjoNCg0KU28gd2UganVzdCBzcGVudCBtb3N0IG9mIG91ciB0aW1lIHRyeWluZyB0byB1bmRlcnN0YW5kIGhvdyB0byB0cmFuc2Zvcm0gb3VyIHNldHMgYmFzZWQgb24gdGhlIGxhcmdlIGFtb3VudHMgb2YgdmFyaWF0aW9uIGluIG91ciBkYXRhLiBUaGVyZSBhcmUgc29tZSBsaW1pdGF0aW9ucyB3ZSd2ZSBkaXNjdXNzZWQgYnV0IGluIHNvbWUgY2FzZXMsIGRlcGVuZGluZyBvbiB5b3VyIGRhdGFzZXQgc2l6ZSB5b3UgbWF5IGJlIGludGVyZXN0ZWQgaW4gZmluZGluZyBzbWFsbCwgbG9jYWwgc2ltaWxhcml0aWVzIHJhdGhlciB0aGFuIHRoZSBsYXJnZSB2YXJpYXRpb24gdGhhdCBjb21lcyB3aXRoIFBDQS4NCg0KVGhlcmUgYXJlIHR3byBwb3B1bGFyIHByb2plY3Rpb25zIHdlJ2xsIGRpc2N1c3MgdG9kYXkgYnV0IGZpcnN0IGxldCdzIHB1dCB0b2dldGhlciBhIG1vcmUgY29tcGxleCBkYXRhc2V0IGJhc2VkIG9uIHNvbWUgUk5Bc2VxIGRhdGEgaW4gYEdTRTE1MDMxNl9EZXNlcU5vcm1Db3VudHNfZmluYWwudHh0YCBhbmQgaXQncyBjb21wYW5pb24gZmlsZSBgMjAyMC4wNy4zMC4yMDE2NTI0MS1zdXBwX3RhYmxlcy54bHN4YA0KDQpgYGB7cn0NCiMgUmVhZCBpbiBvdXIgUk5Bc2VxIGRhdGENCnRpc3N1ZV9kYXRhLmRmIDwtIC4uLihmaWxlID0gLi4uLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxKQ0KIyBUYWtlIGEgcXVpY2sgbG9vayBhdCBpdA0KaGVhZCh0aXNzdWVfZGF0YS5kZikNCmRpbSh0aXNzdWVfZGF0YS5kZikNCg0KIyBSZWFkIGluIHNvbWUgYWRkaXRpb25hbCBwYXRpZW50IGRhdGENCnBhdGllbnRfZGF0YS5kZiA8LSByZWFkX2V4Y2VsKCIuL2RhdGEvMjAyMC4wNy4zMC4yMDE2NTI0MS1zdXBwX3RhYmxlcy54bHN4Iiwgc2hlZXQ9MikNCg0KIyBUYWtlIGEgcXVpY2sgbG9vayBhdCBpdA0KaGVhZChwYXRpZW50X2RhdGEuZGYpDQpkaW0ocGF0aWVudF9kYXRhLmRmKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgNC4wLjEgV2h5IGNhbid0IHdlIHVzZSBQQ0Egb24gdGhpcyBkYXRhc2V0Pw0KDQpCZWZvcmUgd2UgYXR0ZW1wdCBvbmUgb2Ygb3VyIG5vbi1saW5lYXIgcHJvamVjdGlvbiBtZXRob2RzLCBsZXQncyB0cnkgYW5kIHVzZSBzb21lIG9mIHRoZSBtZXRob2RzIHdlJ3ZlIHNwZW50IGFsbCB0aGlzIHRpbWUgbG9va2luZyBhdC4gV2UnbGwgbmFpdmVseSByZXBlYXQgb3VyIFBDQSBzdGVwcyBvbiB0aGlzIHRpc3N1ZSBkYXRhIGFuZCBzZWUgaWYgd2UgY2FuIHB1bGwgYW55IGluZm9ybWF0aW9uIGZyb20gaXQuIFdlIGtub3cgdGhlcmUgYXJlIDEwIHRpc3N1ZSBncm91cHMgaW4gb3VyIGRhdGEgc28gd2UnbGwgdXNlIHRoYXQgYXMgYSBiYXNpcyBmb3IgY2x1c3RlcmluZy4NCg0KOjo6IHthbGlnbj0iY2VudGVyIn0NCjxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vY2Ftb2svQ1NCX0NvdXJzZV9NYXRlcmlhbHMvYmxvYi9tYWluL0FkdlZpei90aXNzdWVJbmZlY3Rpb24uUENBLmttZWFucy5wbmc/cmF3PXRydWUiIHdpZHRoPSI3MDAiLz4NCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KQXMgd2UgY2FuIHNlZSBmcm9tIHRoZSBhYm92ZSBwbG90LCBhIG1ham9yaXR5IG9mIG91ciBzYW1wbGVzIGZyb20gdmFyaW91cyB0aXNzdWUgdHlwZXMgZmFsbCBpbnRvIGEgc2luZ2xlIGNsdXN0ZXIuIFdoYXQncyBtb3JlLCBhcyB3ZSdsbCBzZWUsIHRoZXJlIGlzIHNvbWUgc3RydWN0dXJlIGluIHRoZXNlIHNhbXBsZXMgYXMgbXVsdGlwbGUgc2FtcGxlcyBjb21lIGZyb20gdGhlIHNhbWUgcGF0aWVudC4gU29tZXRpbWVzIGV2ZW4gdGhlIHNhbWUgdGlzc3VlISBUaGVzZSByZWxhdGlvbnNoaXBzIG1heSBoYXZlIGEgbGFyZ2UgZWZmZWN0IGluIHRoZSBvdmVyYWxsIHZhcmlhdGlvbiB3ZSBzZWUgaW4gdGhlIGRhdGEsIG1ha2luZyBpdCBoYXJkIHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gZXZlbiB0aXNzdWUgdHlwZXMuIFRoaXMgaXMgd2h5IHdlJ2xsIHR1cm4gdG8gbm9uLWxpbmVhciBwcm9qZWN0aW9uIGFuZCBzZWUgaWYgaXQgY2FuIGhlbHAgdXMgb3V0LiBCZWZvcmUgdGhhdCB3ZSdsbCBuZWVkIHRvIGRvIHNvbWUgbW9yZSB3cmFuZ2xpbmcuLi4NCg0KIyMjIDQuMC4yIFJlZm9ybWF0IG91ciBwYXRpZW50IG1ldGEgZGF0YQ0KDQpGaXJzdCBsZXQncyBleGFtaW5lIG91ciBgcGF0aWVudF9kYXRhLmRmYCB3aGljaCBoYXMgMjQgcGF0aWVudCBzYW1wbGVzIChha2EgY2FzZXMpIGxpc3RlZCBpbiB0b3RhbC4gVGhlc2UgMjQgY2FzZXMgcmVsYXRlIGJhY2sgdG8gYHRpc3N1ZV9kYXRhLmRmYCB3aGljaCBoYXZlIHZhcmlhYmxlcyByZXByZXNlbnRpbmcgZGlmZmVyZW50IGNvbWJpbmF0aW9ucyBvZiBjYXNlIGFuZCB0aXNzdWUgdHlwZSBhbmQgb2JzZXJ2YXRpb25zIGZvciBzb21lIDU5SyB0cmFuc2NyaXB0cy4NCg0KQXQgdGhpcyBwb2ludCB3ZSB3YW50IHRvIHJlZm9ybWF0IHRoZSBjb2x1bW4gbmFtZXMgaW4gYHBhdGllbnRfZGF0YS5kZmAgYSBiaXQgYmVmb3JlIHNlbGVjdGluZyBmb3IganVzdCB0aGUgdmlyYWwgbG9hZCBhbmQgdmlyYWwgbG9hZCBwZXJjZW50YWdlIGluZm9ybWF0aW9uIGxvY2F0ZWQgaW4gdGhlIDJuZCBhbmQgM3JkIGNvbHVtbi4gV2UnbGwgaG9sZCBvbnRvIHRoaXMgaW4gYHBhdGllbnRfdmlyYWxfbG9hZC5kZmAgZm9yIGxhdGVyIHVzZS4NCg0KYGBge3J9DQojIENyZWF0ZSBhIGRhdGFmcmFtZSBob2xkaW5nIGp1c3QgdGhlIHZpcmFsX2xvYWQgaW5mb3JtYXRpb24gZm9yIGVhY2ggcGF0aWVudA0KcGF0aWVudF92aXJhbF9sb2FkLmRmIDwtDQogIHBhdGllbnRfZGF0YS5kZiAlPiUgDQoNCiAgIyBSZXRhaW4ganVzdCB0aGUgZmlyc3QgMyBjb2x1bW5zDQogIGRwbHlyOjpzZWxlY3QoMTozKSAlPiUgDQoNCiAgIyBSZW5hbWUgdGhlIDJuZCBhbmQgM3JkIGNvbHVtbnMNCiAgLi4uKGNhc2VfbnVtID0gMSwgdmlyYWxfbG9hZCA9IDIsIHZpcmFsX2xvYWRfcGVyY2VudCA9IDMpDQoNCmhlYWQocGF0aWVudF92aXJhbF9sb2FkLmRmKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgNC4wLjMgUHJlcGFyZSBvdXIgUk5Bc2VxIHRpc3N1ZSBkYXRhIGZvciBhbmFseXNpcyBieSBlbGltaW5hdGluZyBmZWF0dXJlcw0KDQpXZSBub3cgd2FudCB0byBmb3JtYXQgYHRpc3N1ZV9kYXRhLmRmYCBtdWNoIGluIHRoZSB3YXkgd2UgZGlkIHdpdGggb3VyIFBIVSBkYXRhLiBXZSB3YW50IHRvIGNvbnZlcnQgb3VyIGN1cnJlbnQgZGF0YSB3aGljaCBsaXN0cyBnZW5lcyBhcyBvYnNlcnZhdGlvbnMgYW5kIHRpc3N1ZSBzYW1wbGVzIGFzIGNvbHVtbnMuIEVzc2VudGlhbGx5LCB3ZSdkIGxpa2UgdG8gdHJhbnNwb3NlIHRoaXMgYW5kIHdlICpjb3VsZCogZG8gdGhhdCBidXQgdGhlIHRyYW5zcG9zaXRpb24gY29udmVydHMgZXZlcnl0aGluZyB0byBhIG1hdHJpeC4gSW4gdGhlIGVuZCwgd2Ugd2FudCB0byB3b3JrIHdpdGggYSBkYXRhIGZyYW1lIHNvIHdlIGNhbiBob2xkIG1vcmUgaW5mb3JtYXRpb24uDQoNCioqKlRvIHJlZHVjZSBvbiBtZW1vcnkgYW5kIHJ1bnRpbWUqKiosIGhvd2V2ZXIsIHdlIHNob3VsZCB0cmltIG91ciBkYXRhc2V0LiBXZSBhcmVuJ3QgcmVhbGx5IGludGVyZXN0ZWQgaW4gbG9va2luZyBhdCA1OSwwOTAgZ2VuZXMgLSBtYW55IG9mIHdoaWNoIG1heSBiZSBiYXJlbHkgZXhwcmVzc2VkLiBTaW5jZSB0aGVzZSBhcmUgbm9ybWFsaXplZCBjb3VudHMgYWNyb3NzIHRoZSBkYXRhc2V0LCB3ZSBjYW4gZmlsdGVyIG91dCBsb3ctZXhwcmVzc2lvbiBnZW5lcyB0byBtYWtlIGB0aXNzdWVfZGF0YV9maWx0ZXJlZC5kZmAuIFllcywgdGhpcyB3b3VsZCBhZ2FpbiBiZSBjb25zaWRlcmVkIGEgZm9ybSBvZiAqZmVhdHVyZSBlbGltaW5hdGlvbiouIEluIGdlbmVyYWwgd2Ugd2lsbDoNCg0KMS4gIENvbnZlcnQgdGhlIHJvdyBuYW1lcyB0byB0aGVpciBvd24gY29sdW1uLg0KMi4gIENhbGN1bGF0ZSB0aGUgbWVhbiBvZiB0aGUgbm9ybWFsaXplZCBjb3VudHMgYWNyb3NzIGVhY2ggb2JzZXJ2YXRpb24gYW5kIGZpbHRlciBmb3IgYSBtaW5pbXVtIG9mIDAuNS4NCjMuICBDYWxjdWxhdGUgdGhlIHZhcmlhbmNlIGFjcm9zcyBlYWNoIG9ic2VydmF0aW9uIGFzIGEgYmFja3VwIHBsYW4uDQoNCmBgYHtyfQ0KDQpzeXN0ZW0udGltZSgNCiMgVHJpbSB0aGUgdGlzc3VlIGRhdGEgZG93bg0KdGlzc3VlX2RhdGFfZmlsdGVyZWQuZGYgPC0NCg0KICB0aXNzdWVfZGF0YS5kZiAlPiUgDQoNCiAgIyBDb252ZXJ0IHRoZSByb3cgbmFtZXMgdG8gYW4gYWN0dWFsIGNvbHVtbg0KICByb3duYW1lc190b19jb2x1bW4odmFyPSJnZW5lIikgJT4lIA0KDQogICMgU2V0IHVwIHRoZSB0YWJsZSB0byBwZXJmb3JtIHJvdy13aXNlIG9wZXJhdGlvbnMNCiAgcm93d2lzZSgpICU+JSANCg0KICAjIENhbGN1bGF0ZSB0aGUgbWVhbiBleHByZXNzaW9uIG9mIGVhY2ggZ2VuZSBhY3Jvc3MgYWxsIHRpc3N1ZSBzYW1wbGVzDQogIG11dGF0ZShtZWFuID0gLi4uKGNfYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpKSkpICU+JSANCg0KICAjIEZpbHRlciBmb3Igc2FtcGxlcyB3aXRoIGxvdyBleHByZXNzaW9uDQogIGZpbHRlcihtZWFuID4gMC41KSAlPiUgDQoNCiAgIyBDYWxjdWxhdGUgb3ZlcmFsbCB2YXJpYW5jZSBpbiBjYXNlIHdlIG5lZWQgdG8gbWFrZSBvdXIgZGF0YXNldCBzbWFsbGVyDQogIG11dGF0ZSh2YXJpYW5jZSA9IC4uLihjX2Fjcm9zcyh3aGVyZShpcy5udW1lcmljKSkpKSAlPiUgDQoNCiAgIyBBcnJhbmdlIHNhbXBsZXMgYnkgZGVzY2VuZGluZyB2YXJpYW5jZQ0KICBhcnJhbmdlKGRlc2ModmFyaWFuY2UpKSAlPiUgDQoNCiAgIyBSZW1vdmUgdGhlIGdyb3VwaW5nIHNwZWNpZmljYXRpb24NCiAgdW5ncm91cCgpDQoNCikNCg0KIyBUYWtlIGEgbG9vayBhdCB0aGUgZmluYWwgcmVzdWx0cw0KaGVhZCh0aXNzdWVfZGF0YV9maWx0ZXJlZC5kZikNCg0KIyBob3cgYmlnIGlzIG91ciBmaWx0ZXJlZCBkYXRhIGZyYW1lPw0KZGltKHRpc3N1ZV9kYXRhX2ZpbHRlcmVkLmRmKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgNC4wLjQgTW9yZSBkYXRhIHdyYW5nbGluZyB0byB0cmFuc3Bvc2Ugb3VyIFJOQXNlcSBkYXRhDQoNCk5vdyB0aGF0IHdlJ3ZlIGZpbHRlcmVkIG91ciBkYXRhIGRvd24gdG8gXH4yOWsgZ2VuZXMsIHdlJ2xsIHJ1biB0aHJvdWdoIHR3byBtb3JlIHN0ZXBzOg0KDQoxLiAgV2UnbGwgdHJhbnNwb3NlIG91ciBkYXRhIHVzaW5nIGEgY29tYmluYXRpb24gb2YgYHBpdm90X2xvbmdlcigpYCBhbmQgYHBpdm90X3dpZGVyKClgLg0KMi4gIFdlJ2xsIHVzZSBhIHNlcmllcyBvZiBzdHJpbmcgbWF0Y2hlcyBhbmQgam9pbnMgdG8gYWRkIHNvbWUgc2FtcGxlIGluZm9ybWF0aW9uIHRvIG91ciBkYXRhLiBJdCB3b24ndCBiZSB1c2VkIGZvciBvdXIgYW5hbHlzaXMgYnV0IHdpbGwgYmUgdXNlZCB3aGVuIHdlIHBsb3QgdGhlIHJlc3VsdHMhDQozLiAgTGFzdGx5IHdlJ2xsIGNvbnZlcnQgdGhlIHJlbGV2YW50IHBhcnRzIG9mIG91ciBkYXRhIGZyYW1lIHRvIGEgbWF0cml4LiBUaGVyZSBpcyBhIGh1Z2UgbWVtb3J5IHNhdmluZ3MgaW4gdGhlIGFsZ29yaXRobSB3aGVuIHdvcmtpbmcgd2l0aCBhIG1hdHJpeCBhbmQgeW91IGhhdmUgbGltaXRlZCBtZW1vcnkgb24geW91ciBSU3R1ZGlvIEh1YnMhDQoNCmBgYHtyfQ0KIyBZb3UgbmVlZCB0byB0cmFuc3Bvc2UgdGhlIGRhdGEuIA0KIyBXZSBjYW4gZG8gaXQgd2l0aCBkcGx5ciB0byBrZWVwIGl0IGFzIGEgZGF0YSBmcmFtZSBhbmQgdG8gYWRkIHNvbWUgaW5mbw0KDQp0aXNzdWVfUk5Bc2VxLmRmIDwtDQoNCiAgdGlzc3VlX2RhdGFfZmlsdGVyZWQuZGYgJT4lIA0KICANCiAgIyB0cmltIGRvd24gdGhlIGNvbHVtbnMgdG8gZHJvcCBtZWFuL3ZhcmlhbmNlDQogIGRwbHlyOjpzZWxlY3QoMTo4OSkgJT4lIA0KDQogICMgcGl2b3QgbG9uZ2VyDQogIHBpdm90X2xvbmdlcihjb2xzPWMoMjo4OSksIG5hbWVzX3RvID0gLi4uLCB2YWx1ZXNfdG8gPSAuLi4pICU+JSANCg0KICAjIHJlZGlzdHJpYnV0ZSB0aGUgZ2VuZSBuYW1lcyB0byBjb2x1bW5zDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBnZW5lLCB2YWx1ZXNfZnJvbSA9IC4uLikNCmBgYA0KDQpgYGB7cn0NCiMgV2Ugd2FudCB0byBhZGQgc29tZSBhZGRpdGlvbmFsIHNhbXBsZSBpbmZvcm1hdGlvbiBiZWZvcmUgYXNzZXNzaW5nIHRoZSBkYXRhDQoNCnRpc3N1ZV9STkFzZXEuZGYgPC0NCg0KICB0aXNzdWVfUk5Bc2VxLmRmICU+JSANCg0KICAjIEdyYWIganVzdCB0aGUgc2FtcGxlIG5hbWVzDQogIGRwbHlyOjpzZWxlY3Qoc2FtcGxlKSAlPiUgDQoNCiAgIyBHcmFiIGluZm9ybWF0aW9uIGZyb20gaXQgbGlrZSBjYXNlIG51bWJlciwgdGlzc3VlLCBhbmQgdGlzc3VlIG51bWJlcg0KICAjIHRha2VzIHRoZSBmb3JtIG9mICJjYXNlWC50aXNzdWVZIiBvciAiY2FzZVgudGlzc3VlLk5ZQyIgb3IgIk5lZ0NvbnRyb2xYIg0KICAjIFJlbWVtYmVyIHRoYXQgdGhpcyByZXR1cm5zIGEgTElTVCBvZiBjaGFyYWN0ZXIgbWF0cmljZXMNCiAgc3RyX21hdGNoX2FsbCguLCBwYXR0ZXJuPXIiKGNhc2UoW1x3XSspXC4oW2Etel0rKShbXGR8XC5OWUNdKil8KE5lZ0NvbnRyb2xcZCkpIikgJT4lIA0KDQogICMgQmluZCBhbGwgdGhlIG1hdHJpY2VzIGZyb20gYWxsIHRoZSBsaXN0IGVsZW1lbnRzIHRvZ2V0aGVyIGluIGEgc2luZ2xlIG9iamVjdCAobGlrZWx5IGEgbWF0cml4KQ0KICBkby5jYWxsKHJiaW5kLCAuKSAlPiUgDQoNCiAgIyBDb252ZXJ0IHRoZSByZXN1bHRzIHRvIGEgZGF0YSBmcmFtZQ0KICBhcy5kYXRhLmZyYW1lKCkgJT4lIA0KDQogICMgUmVuYW1lIHRoZSBjb2x1bW5zIGJhc2VkIG9uIHRoZSBjYXB0dXJlIGdyb3Vwcw0KICBkcGx5cjo6cmVuYW1lKC4sIHNhbXBsZSA9IFYxLCBjYXNlX251bSA9IFYyLCB0aXNzdWUgPSBWMywgdGlzc3VlX251bSA9IFY0LCBuZWdfbnVtID0gVjUpICU+JQ0KDQogICMgQ29hbGVzY2Ugc29tZSBvZiB0aGUgaW5mbyBkdWUgdG8gbmVnYXRpdmUgY29udHJvbCBzYW1wbGVzIGFuZCBjbGVhbiB1cCBhIGNvbHVtbg0KICBtdXRhdGUoY2FzZV9udW0gPSBjb2FsZXNjZShjYXNlX251bSwgbmVnX251bSksDQogICAgICAgICB0aXNzdWVfbnVtID0gc3RyX3JlcGxhY2VfYWxsKC4kdGlzc3VlX251bSwgcGF0dGVybiA9ICJcXC4iLCByZXBsYWNlID0gIiIpKSAlPiUNCiAgDQogICMgRHJvcCB0aGUgbmVnX251bSBjb2x1bW4NCiAgZHBseXI6OnNlbGVjdCgxOjQpICU+JQ0KICANCiAgIyBKb2luIHRoaXMgcmVzdWx0IHRvIHRoZSBSTkFzZXEgaW5mbw0KICBmdWxsX2pvaW4oLiwgeT10aXNzdWVfUk5Bc2VxLmRmLCBieT1jKCJzYW1wbGUiID0gInNhbXBsZSIpKSAlPiUNCiAgDQogICMgSm9pbiB0aGF0IHJlc3VsdCB0byBncmFiIHZpcmFsIGxvYWQgaW5mb3JtYXRpb24NCiAgcmlnaHRfam9pbihwYXRpZW50X3ZpcmFsX2xvYWQuZGYsIHk9LiwgYnk9YygiY2FzZV9udW0iID0gImNhc2VfbnVtIikpDQoNCiMgTG9vayBhdCB0aGUgcmVzdWx0aW5nIGRhdGFmcmFtZQ0KaGVhZCh0aXNzdWVfUk5Bc2VxLmRmKQ0KYGBgDQoNCmBgYHtyfQ0KIyBIb3cgbWFueSB0aXNzdWUgdHlwZXMgZG8gd2UgaGF2ZT8NCnRhYmxlKHRpc3N1ZV9STkFzZXEuZGYkdGlzc3VlKQ0KYGBgDQoNCmBgYHtyfQ0KIyBHZW5lcmF0ZSBhIG1hdHJpeCB2ZXJzaW9uIG9mIG91ciBkYXRhIGJ1dCBkcm9wIHRoZSBzYW1wbGUgbWV0YWRhdGEhDQp0aXNzdWVfUk5Bc2VxLm14IDwtIGFzLm1hdHJpeCh0aXNzdWVfUk5Bc2VxLmRmWywuLi5dKQ0KDQojIGhlYWQodGlzc3VlX1JOQXNlcS5teCkNCnN0cih0aXNzdWVfUk5Bc2VxLm14KQ0KZGltKHRpc3N1ZV9STkFzZXEubXgpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDQuMS4wIHQtRGlzdHJpYnV0ZWQgU3RvY2hhc3RpYyBOZWlnaGJvdXIgRW1iZWRkaW5nIHdpdGggdGhlIGBSdHNuZWAgcGFja2FnZQ0KDQpXZSBub3cgaGF2ZSBhIHNvbWV3aGF0IG1vcmUgY29tcGxleCBkYXRhc2V0LiBXZSBhcmUgc3RpbGwgc2hvcnQgb24gYWN0dWFsIHNhbXBsZXMgKG5vdyBvYnNlcnZhdGlvbnMpIGJ1dCA4OCBvYnNlcnZhdGlvbnMgYW5kIG5lYXJseSAzMEsgZmVhdHVyZXMgaXNuJ3Qgc28gYmFkLiBBIGJyb2FkIHF1ZXN0aW9uIHdlIG1heSB3aXNoIHRvIGFzayB3aXRoIHN1Y2ggYSBkYXRhIHNldCBpcyBpZiB0aGVyZSBpcyBhbiB1bmRlcmx5aW5nIHN0cnVjdHVyZSB0byB0aGVzZSBzYW1wbGVzIC0gaS5lLiBkbyB3ZSBzZWUgZ3JvdXBpbmcgYmFzZWQgb24gdGlzc3VlIHR5cGUsIG9yIHBlcmhhcHMgZXZlbiBzYW1wbGUgcHJlcGFyYXRpb24uDQoNCioqdC1EaXN0cmlidXRlZCBTdG9jaGFzdGljIE5laWdoYm91ciBFbWJlZGRpbmcqKiBvciAqKnQtU05FKiogaXMgYSB3YXkgZm9yIHVzIHRvIHByb2plY3Qgb3VyIGhpZ2gtZGltZW5zaW9uIGRhdGEgb250byBhIGxvd2VyIGRpbWVuc2lvbiB3aXRoIHRoZSBhaW0gYXQgKnByZXNlcnZpbmcgdGhlIGxvY2FsIHNpbWlsYXJpdGllcyByYXRoZXIgdGhhbiBnbG9iYWwgZGlzcGFyaXR5Ki4gV2hlbiBsb29raW5nIGF0IGRhdGEgcG9pbnRzLCB0LVNORSB3aWxsIGF0dGVtcHQgdG8gcHJlc2VydmUgdGhlIGxvY2FsIG5laWdoYm91cmluZyBzdHJ1Y3R1cmUuIEFzIHRoZSBhbGdvcml0aG0gcG91cnMgb3ZlciB0aGUgZGF0YSwgaXQgY2FuIHVzZSBkaWZmZXJlbnQgdHJhbnNmb3JtYXRpb25zIGZvciBkaWZmZXJlbnQgcmVnaW9ucyBhcyBpdCBhdHRlbXB0cyB0byB0cmFuc2Zvcm0gZXZlcnl0aGluZyB0byBhIGxvd2VyIGRpbWVuc2lvbi4gSXQgaXMgY29uc2lkZXJlZCAiaW5jcmVkaWJseSBmbGV4aWJsZSIgYXQgZmluZGluZyBsb2NhbCBzdHJ1Y3R1cmUgd2hlcmUgb3RoZXIgYWxnb3JpdGhtcyBtYXkgbm90Lg0KDQpUaGlzIGZsZXhpYmlsaXR5IGlzIGFjY29tcGxpc2hlZCB0aHJvdWdoIDIgc3RlcHM6DQoNCjAuICAqUmVkdWNlIGRpbWVuc2lvbmFsaXR5IG9mIHlvdXIgZGF0YSBmZWF0dXJlcyB3aXRoIFBDQSEqDQoxLiAgR2VuZXJhdGUgYSBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb24gYmV0d2VlbiBhbGwgcGFpcnMgYnkgbWFraW5nIHNpbWlsYXIgb2JqZWN0cyBoaWdobHkgcHJvYmFibHkgYW5kIGFzc2lnbmluZyBkaXNzaW1pbGFyIG9iamVjdHMgYSBsb3cgcHJvYmFiaWxpdHkuDQoyLiAgRGVmaW5lIGEgc2ltaWxhciBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb24gZm9yIHRoZSBzYW1wbGVzIGluIGEgbG93ZXIgZGltZW5zaW9uIHdoaWxlIG1pbmltaXppbmcgdGhlIGRpdmVyZ2VuY2UgYmV0d2VlbiB0aGUgdHdvIGRpc3RyaWJ1dGlvbnMgYmFzZWQgb24gYSBkaXN0YW5jZSBtZXRyaWMgYmV0d2VlbiBwb2ludHMgaW4gdGhlIGxvd2VyIGRpbWVuc2lvbi4NCg0KV2UnbGwgZGlzY3VzcyBtb3JlIGFib3V0IGhvdyB0aGlzIGFsZ29yaXRobSBhZmZlY3RzIGludGVycHJldGF0aW9uIGFmdGVyIHNlZWluZyB0aGUgcmVzdWx0cywgYnV0IHRoaXMgaXMgY29uc2lkZXJlZCBhbiAqKipleHBsb3JhdG9yeSoqKiBkYXRhIHZpc3VhbGl6YXRpb24gdG9vbCwgcmF0aGVyIHRoYW4gKioqZXhwbGFuYXRvcnkqKiouDQoNClRvIHByb2R1Y2Ugb3VyIHQtU05FIHByb2plY3Rpb24gd2UnbGwgdXNlIHRoZSBgUnRzbmUoKWAgZnVuY3Rpb24gZnJvbSB0aGUgcGFja2FnZSBvZiB0aGUgc2FtZSBuYW1lLiBTb21lIGltcG9ydGFudCBwYXJhbWV0ZXJzIGFyZToNCg0KLSAgIGBYYCBpcyBvdXIgZGF0YSBtYXRyaXggd2hlcmUgZWFjaCByb3cgaXMgYW4gb2JzZXJ2YXRpb24NCg0KLSAgIGBkaW1zYCBzZXRzIHRoZSBudW1iZXIgb2YgZGltZW5zaW9ucyB3ZSdkIGxpa2UgdG8gcHJvamVjdCBvbnRvIChkZWZhdWx0IGlzIDIpLg0KDQotICAgYGluaXRpYWxfZGltc2Agc2V0cyB0aGUgbnVtYmVyIG9mIGRpbWVuc2lvbnMgdGhhdCBzaG91bGQgYmUgcmV0YWluZWQgaW4gdGhlIGluaXRpYWwgUENBIHN0ZXAgKERlZmF1bHQgNTApLg0KDQotICAgYHBlcnBsZXhpdHlgIGEgbnVtZXJpYyBwYXJhbWV0ZXIgdGhhdCB0dW5lcyBiZXR3ZWVuIGxvY2FsIGFuZCBnbG9iYWwgYXNwZWN0cyBvZiB5b3VyIGRhdGEuDQoNCiAgICAtICAgVGhpcyBwYXJhbWV0ZXIgaXMgYSBndWVzcyBhcyB0byBob3cgbWFueSBjbG9zZSBuZWlnaGJvdXJzIGEgcG9pbnQgbWF5IGhhdmUuIElmIHlvdSBoYXZlIGEgc2Vuc2Ugb2Ygc2FtcGxlIHR5cGVzIChvciBjbHVzdGVycyEpIGFoZWFkIG9mIHRpbWUsIHlvdSBjb3VsZCB0cnkgdG8gcGxheSB3aXRoIHRoaXMgdmFsdWUgKGRlZmF1bHQgaXMgMzApLg0KDQogICAgLSAgIEFjY29yZGluZyB0byB0aGUgYWxnb3JpdGhtIHRoaXMgdmFsdWUgc2hvdWxkIGZvbGxvdyB0aGlzIHJ1bGU6ICRwZXJwbGV4aXR5ICogMyBcbHQgbnJvdyhYKSAtMSQNCg0KLSAgIGBwY2Ffc2NhbGVgIGlzIGEgYm9vbGVhbiB0byBzZXQgaWYgdGhlIGluaXRpYWwgUENBIHN0ZXAgc2hvdWxkIHVzZSBzY2FsZWQgZGF0YS4NCg0KLSAgIGBtYXhfaXRlcmAgaXMgdGhlIG1heGltdW0gbnVtYmVyIG9mIGl0ZXJhdGlvbnMgaW4gdGhlIGFsZ29yaXRobSAoZGVmYXVsdCBpcyAxMDAwKS4NCg0KYGBge3J9DQojIFJ0c25lIHByZWZlcnMgdXNpbmcgYSBtYXRyaXggZm9yIG1lbW9yeSBpc3N1ZXMNCiMgc2V0IGEgc2VlZCBmb3IgcmVwcm9kdWNpYmxlIHJlc3VsdHMNCnNldC5zZWVkKDIwMjQpDQoNCiMgVHJ5IGZvciBwZXJwbGV4aXR5IG9mIDMwIGNhbiBnbyBhcyBoaWdoIGFzIDI5IGJlZm9yZSBjcmFzaA0KIyBXZSBoYXZlIGp1c3QgOTAgc2FtcGxlcywgYnV0IGJldHdlZW4gMS01MiBzYW1wbGVzIHBlciAiZ3JvdXAiDQp0aXNzdWVfdHNuZSA8LSBSdHNuZSguLi4sIA0KICAgICAgICAgICAgICAgICAgICAgZGltcz0yLCANCiAgICAgICAgICAgICAgICAgICAgIHBlcnBsZXhpdHk9NSwgDQogICAgICAgICAgICAgICAgICAgICB2ZXJib3NlPVRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgcGNhX3NjYWxlID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgIG1heF9pdGVyID0gMTUwMCkgDQpgYGANCg0KYGBge3J9DQojIFdoYXQgZG9lcyBvdXIgdC1TTkUgb2JqZWN0IGxvb2sgbGlrZT8NCnN0ciguLi4pDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyA0LjEuMSBFeHRyYWN0IGluZm9ybWF0aW9uIGZyb20gb3VyIHRzbmUgb2JqZWN0DQoNCkxvb2tpbmcgYWJvdmUgYXQgdGhlIHJlc3VsdCBvZiBvdXIgdC1TTkUgYW5hbHlzaXMsIHdlIGNhbiBub3RpY2UgYSBmZXcgdGhpbmdzLi4uDQoNCjEuICBXZSBnZXQgYmFjayB0aGUgbnVtYmVyIG9mIG9iamVjdHMgd2UgcHV0IGluOiA4OC4NCjIuICBUaGUgZWxlbWVudCBgWWAgaXMgYW4gODh4MiBtYXRyaXggaG9sZGluZyB0aGUgY29vcmRpbmF0ZXMgZm9yIG91ciBuZXcgcHJvamVjdGlvbi4NCjMuICBUaGVyZSBhcmUgbm8gZWlnZW52ZWN0b3JzIG9yIG90aGVyIGluZm9ybWF0aW9uIGFib3V0IHRoZSB2YXJpYWJsZXMgb3IgZGltZW5zaW9ucyBvciBob3cgdGhleSB3ZXJlIHVzZWQgaW4gdGhlIHByb2plY3Rpb24hDQoNClRoYXQncyByaWdodCwgdGhlcmUgaXMgbm8gdW5kZXJseWluZyBpbmZvcm1hdGlvbiBmb3IgbWFwcGluZyBiYWNrIHRvIG91ciBvcmlnaW5hbCBkYXRhc2V0LiBJdCdzIGEgY29tcGxldGVseSBibGFjayBib3ggd2l0aCBubyB3YXkgdG8gcmV2ZXJzZSBlbmdpbmVlciB0aGUgcHJvY2Vzcy4gVGhhdCdzIGJlY2F1c2UgdGhlIHByb2Nlc3MgaXRzZWxmIGlzICoqKnN0b2NoYXN0aWMqKiohIFdoZXJlYXMgUENBIHdhcyBhIGRldGVybWluaXN0aWMgcHJvY2VzcyAtIHJlcGVhdGFibGUgdGhlIHNhbWUgd2F5IGV2ZXJ5IHRpbWUgd2l0aCB0aGUgc2FtZSBkYXRhIC0gdGhhdCBpcyBub3QgdGhlIGNhc2UgZm9yIHQtU05FLiBUaGF0J3Mgd2h5IGV2ZW4gdGhvdWdoIGl0IGNhbiBiZSBxdWl0ZSBwb3dlcmZ1bCBpbiBpZGVudGlmeWluZyBsb2NhbCBzaW1pbGFyaXRpZXMsICoqKnQtU05FIGRvZXMgbm90IHByb3ZpZGUgYSBtYXRoZW1hdGljYWwgcGF0aHdheSBiYWNrIHRvIG91ciBvcmlnaW5hbCBkYXRhISoqKg0KDQpMZXQncyBleHRyYWN0IHRoZSBpbmZvcm1hdGlvbiwgY29tYmluZSBpdCB3aXRoIG91ciBzYW1wbGUgaW5mb3JtYXRpb24gYW5kIHByb2plY3QgaXQgdXNpbmcgYGdncGxvdDJgLiBXZSBjYW4gZG8gdGhpcyB3aXRoIGEgc2NhdHRlcnBsb3Qgc2luY2Ugd2UgaGF2ZSBhIHNldCBvZiB4L3kgY29vcmRpbmF0ZXMgd2UgY2FuIHdvcmsgd2l0aC4NCg0KYGBge3J9DQojIEJ1aWxkIG91ciBuZXcgZGF0YSBmcmFtZSBmcm9tIHRoZSBZIHZhbHVlcw0KdGlzc3VlX3RzbmUuZGYgPC0gZGF0YS5mcmFtZSh4LmNvb3JkID0gLi4uLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5LmNvb3JkID0gLi4uKQ0KDQojIEFkZCBpbiBvdXIgc2FtcGxlIGluZm9ybWF0aW9uDQp0aXNzdWVfdHNuZS5kZiA8LSBjYmluZCh0aXNzdWVfUk5Bc2VxLmRmWywxOjZdLCB0aXNzdWVfdHNuZS5kZikNCg0KIyBGaXggdXAgdGhlIGluZm9ybWF0aW9uIGp1c3QgYSBsaXR0bGUgYml0IHRvIHJlbW92ZSBOQSB2aXJhbCBsb2FkIGluZm9ybWF0aW9uDQp0aXNzdWVfdHNuZS5kZiA8LQ0KICB0aXNzdWVfdHNuZS5kZiAlPiUgDQogICMgcmVwbGFjZSBOQXMgd2l0aCBETlcgKGRpZCBub3Qgd29yaykNCiAgbXV0YXRlKHZpcmFsX2xvYWQgPSByZXBsYWNlX25hKHZpcmFsX2xvYWQsIHJlcGxhY2UgPSAiRE5XIikpDQoNCmhlYWQodGlzc3VlX3RzbmUuZGYpDQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0yMH0NCg0KY29tYm8uY29sb3VycyA9IGMoYnJld2VyLnBhbCgxMiwgIlBhaXJlZCIpLCBicmV3ZXIucGFsKDEyLCAiU2V0MyIpLCBicmV3ZXIucGFsKDgsICJTZXQxIikpDQoNCiMgMS4gRGF0YQ0KZ2dwbG90KGRhdGEgPSB0aXNzdWVfdHNuZS5kZikgKw0KICAjIDIuIEFlc3RoZXRpY3MNCiAgYWVzKHggPSB4LmNvb3JkLCANCiAgICAgIHkgPSB5LmNvb3JkLCANCiAgICAgIGNvbG91ciA9IC4uLikgKw0KDQogICMgVGhlbWVzDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApKSArDQoNCiAgIyAzLiBTY2FsaW5nDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gY29tYm8uY29sb3VycykgKw0KDQogICMgNC4gR2VvbXMNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGNhc2VfbnVtKSwgc2l6ZSA9IDEwKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgNC4xLjIgSW50ZXJwcmV0aW5nIG91ciB0LVNORSBwbG90DQoNCldoaWxlIHdlIGRvbid0IGhhdmUgYSBsb3Qgb2Ygc2FtcGxlcywgeW91IGNhbiBzdGlsbCBzZWUgdGhhdCB3ZSB3ZXJlIGFibGUgdG8gY2x1c3RlciAqc29tZSogb2Ygb3VyIGRhdGEgYnkgY2VsbCB0eXBlcyB3aXRob3V0IHByb3ZpZGluZyB0aGF0IGNsYXNzaWZpY2F0aW9uIHRvIHRoZSBhbGdvcml0aG0hIEdyZWF0IGpvYiB0ZWFtIQ0KDQpXZSBjYW4gc2VlIHRoYXQgd2UgZ2V0IGNsb3NlIGNsdXN0ZXJpbmcgb2YgdGlzc3VlcyBsaWtlIHBsYWNlbnRhLCBoZWFydCwgYW5kIGJvd2VsLiBPdXIgbGl2ZXIgc2FtcGxlcyBhcmUga2luZCBvZiBldmVyeXdoZXJlIGJ1dCBwZXJoYXBzIHVzaW5nIGEgZGlmZmVyZW50IHBlcnBsZXhpdHkgd291bGQgcHJvdmlkZSBkaWZmZXJlbnQgcmVzdWx0cy4NCg0KT25lIGludGVyZXN0aW5nIHRoaW5nIHdlIGNhbiBzZWUgaXMgdGhhdCByZWdhcmRsZXNzIG9mIHRpc3N1ZSB0eXBlLCB3ZSBzZWUgc29tZSBzYW1wbGVzIGFyZSBjbHVzdGVyaW5nIHRvZ2V0aGVyIGJhc2VkIG9uIGNhc2UgbnVtYmVyIC0gbmFtZWx5IGNhc2UgbnVtYmVycyAxLCA0IGFuZCA1IHNlZW0gdG8gaGF2ZSBzb21lIHN0cm9uZyB1bmRlcmx5aW5nIGV4cHJlc3Npb24gcHJvZmlsZXMgdGhhdCBjb25uZWN0IHRoZW0gYWNyb3NzIHRpc3N1ZSBzYW1wbGVzLiBXZSBtYXkgYWxzbyBiZSBzZWVpbmcgZmFsc2UgcmVsYXRpb25zaGlwcyBzbyBiZXdhcmUhDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA0LjIuMCBVbmlmb3JtIE1hbmlmb2xkIEFwcHJveGltYXRpb24gYW5kIFByb2plY3Rpb24NCg0KVGhpcyBhbGdvcml0aG0gZm9yIHByb2plY3Rpb24gaXMgaW4gdGhlIHNhbWUgZmxhdm91ciBhcyB0LVNORSBwcm9qZWN0aW9uIGJ1dCBoYXMgc29tZSBkaWZmZXJlbmNlcyBpbmNsdWRpbmc6DQoNCjEuICBJbmNyZWFzZWQgc3BlZWQgYW5kIGJldHRlciBwcmVzZXJ2YXRpb24gb2YgdGhlIGdsb2JhbCBzdHJ1Y3R1cmUgaW4geW91ciBkYXRhDQoyLiAgQSBkaWZmZXJlbnQgdGhlb3JldGljYWwgZm91bmRhdGlvbiB1c2VkIHRvIGJhbGFuY2UgYmV0d2VlbiBsb2NhbCBhbmQgZ2xvYmFsIHN0cnVjdHVyZQ0KDQpXaGF0IGRvZXMgdGhhdCBtZWFuIGZvciB1cz8gRmFzdGVyIHJlc3VsdHMsIGFuZCBtb3JlIGludGVycHJldGl2ZSByZXN1bHRzISBPdGhlcndpc2UgdGhlIHNhbWUgaXNzdWVzIGNhbiBhcHBseS4gVGhlIHNldHVwIGlzIHNsaWdodGx5IGVhc2llciB3aXRoIGZldyBvcHRpb25zIHRvIGNoYW5nZSBpZiB5b3UgbGVhdmUgdGhlIGRlZmF1bHRzLiBXZSBjYW4gYWNjZXNzIGB1bWFwKClgIGZyb20gdGhlIHBhY2thZ2Ugb2YgdGhlIHNhbWUgbmFtZS4gWW91IG1heSBhbHNvIGFsdGVyIHRoZSBkZWZhdWx0IG1ldGhvZHMgYnkgY3JlYXRpbmcgYSBgdW1hcC5kZWZhdWx0c2Agb2JqZWN0LiBNb3JlIGluZm9ybWF0aW9uIG9uIHRoYXQgW2hlcmVdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy91bWFwL3VtYXAucGRmKQ0KDQpGb3IgbW9yZSB0aW5rZXJpbmcsIHlvdSBjYW4gY2hvb3NlIHRvIHVzZSB0aGUgYHV3b3RgIFtwYWNrYWdlXShodHRwczovL3JkcnIuaW8vY3Jhbi91d290LykgaW5zdGVhZCB3aGVyZSB0aGUgYHVtYXAoKWAgZnVuY3Rpb24gaGFzIG1vcmUgb3B0aW9ucyB0aGF0IGFyZSBlYXNpbHkgbW9kaWZpZWQuDQoNCmBgYHtyfQ0KIyBTZXQgb3VyIHNlZWQNCnNldC5zZWVkKDIwMjQpDQoNCiMgR2VuZXJhdGUgb3VyIHByb2plY3Rpb24NCnRpc3N1ZV91bWFwIDwtIC4uLih0aXNzdWVfUk5Bc2VxLm14KQ0KYGBgDQoNCmBgYHtyfQ0KIyBXaGF0IGRvZXMgdGhlIFVNQVAgb2JqZWN0IGxvb2sgbGlrZT8NCnN0cih0aXNzdWVfdW1hcCkNCmBgYA0KDQojIyMgNC4yLjEgRXh0cmFjdCBpbmZvcm1hdGlvbiBmcm9tIG91ciBVTUFQIG9iamVjdA0KDQpMb29raW5nIGF0IG91ciBVTUFQIG9iamVjdCBgdGlzc3VlX3VtYXBgIHdlIHNlZSBpdCBob3VzZXMgdGhlIHByb2plY3Rpb24gcGFyYW1ldGVycyB1c2VkIGJ1dCBhbHNvIHNvbWUgYWRkaXRpb25hbCB2YXJpYWJsZXM6DQoNCjEuICBgZGF0YWA6IGhvbGRzIG91ciBvcmlnaW5hbCBkYXRhIG1hdHJpeC4NCjIuICBgbGF5b3V0YDogY29udGFpbnMgdGhlIHByb2plY3Rpb24gY29vcmRpbmF0ZXMgd2UgbmVlZCBmb3IgcGxvdHRpbmcgdGhlIGRhdGEuDQozLiAgYGtubmA6IGEgd2VpZ2h0ZWQgay1uZWFyZXN0IG5laWdoYm91ciBncmFwaC4gVGhpcyBpcyBhIGdyYXBoIHRoYXQgY29ubmVjdHMgZWFjaCBvYnNlcnZhdGlvbiB0byBpdHMgbmVhcmVzdCBrIG5laWdoYm91cnMuIFRoaXMgZ2VuZXJhdGVzIHRoZSBmaXJzdCB0b3BvbG9naWNhbCByZXByZXNlbnRhdGlvbiBvZiB0aGUgZGF0YSAtIGxpa2UgYW4gaW5pdGlhbCBza2V0Y2guDQoNCllvdSBtYXkgbm90aWNlIGFnYWluIHRoYXQgdGhlcmUgaXMgbm8gZGF0YSB0aGF0IHN1Z2dlc3RzIGhvdyB3ZSAqKiphcnJpdmVkKioqIGF0IHRoaXMgc29sdXRpb24uIFRoZXJlIGFyZSAqbm8gZWlnZW52ZWN0b3JzIG9yIHZhbHVlcyogdG8gcmV2ZXJzZSB0aGUgcHJvamVjdGlvbiENCg0KTGV0J3MgZXh0cmFjdCB0aGUgYGxheW91dGAgaW5mb3JtYXRpb24sIGNvbWJpbmUgaXQgd2l0aCBvdXIgc2FtcGxlIGluZm9ybWF0aW9uIGFuZCBwcm9qZWN0IGl0IHVzaW5nIGBnZ3Bsb3QyYA0KDQpgYGB7cn0NCiMgUmUtbWFwIG91ciBwcm9qZWN0aW9uIHBvaW50cyB3aXRoIG91ciB0aXNzdWUgZGF0YQ0KDQp0aXNzdWVfdW1hcC5kZiA8LSBkYXRhLmZyYW1lKHguY29vcmQgPSB0aXNzdWVfdW1hcCQuLi4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkuY29vcmQgPSB0aXNzdWVfdW1hcCQuLi4pDQoNCnRpc3N1ZV91bWFwLmRmIDwtIGNiaW5kKHRpc3N1ZV9STkFzZXEuZGZbLDE6Nl0sIHRpc3N1ZV91bWFwLmRmKQ0KDQp0aXNzdWVfdW1hcC5kZiA8LQ0KICB0aXNzdWVfdW1hcC5kZiAlPiUgDQogICMgcmVwbGFjZSBOQXMgd2l0aCBETlcgKGRpZCBub3Qgd29yaykNCiAgbXV0YXRlKHZpcmFsX2xvYWQgPSByZXBsYWNlX25hKHZpcmFsX2xvYWQsIHJlcGxhY2UgPSAiRE5XIikpDQoNCmhlYWQodGlzc3VlX3VtYXAuZGYpDQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0yMH0NCg0KY29tYm8uY29sb3VycyA9IGMoYnJld2VyLnBhbCgxMiwgIlBhaXJlZCIpLCBicmV3ZXIucGFsKDEyLCAiU2V0MyIpLCBicmV3ZXIucGFsKDgsICJTZXQxIikpDQoNCiMgMS4gRGF0YQ0KZ2dwbG90KGRhdGEgPSAuLi4pICsNCiAgIyAyLiBBZXN0aGV0aWNzDQogIGFlcyh4ID0geC5jb29yZCwgDQogICAgICB5ID0geS5jb29yZCwgDQogICAgICBjb2xvdXIgPSB0aXNzdWUpICsNCg0KICAjIFRoZW1lcw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSkgKw0KDQogICMgMy4gU2NhbGluZw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGNvbWJvLmNvbG91cnMpICsNCg0KICAjIDQuIEdlb21zDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBjYXNlX251bSksIHNpemUgPSAxMCkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDQuMi4yIEludGVycHJldGluZyBvdXIgVU1BUCByZXN1bHQNCg0KU28gaXQgbG9va3MgbGlrZSB3aXRob3V0IG11Y2ggdGlua2VyaW5nIHdlIHJldHJpZXZlZCBhIGZhaXJseSBuaWNlIHJlc3VsdC4gV2UgY2FuIHNlZSBub3cgdGhhdCBsaXZlciBhbmQga2lkbmV5IGFyZSBtb3JlIGNsb3NlbHkgZ3JvdXBlZCBhcyB0aXNzdWVzLCB3aGlsZSBoZWFydCBzYW1wbGVzIGdlbmVyYWxseSBjbHVzdGVyIHRvZ2V0aGVyIHN0aWxsLiBCb3dlbCBhbmQgamVqdW51bSBhcHBlYXIgc3BhdGlhbGx5IGdyb3VwZWQgYW5kIG91ciBwbGFjZW50YSBzYW1wbGVzIGFyZSBzdGlsbCBjbG9zZSB0byBlYWNoIG90aGVyLiBUaGUgY2x1c3RlcmluZyB3ZSBzYXcgd2l0aCBzYW1wbGVzIDEsIDQsIGFuZCA1IGFwcGVhciB0byBiZSBsZXNzIHNldmVyZS4NCg0KVGhlcmUgZG9lcyBhcHBlYXIgdG8gYmUgc29tZSBzdHJ1Y3R1cmUgYmV0d2VlbiB0aGUgbHVuZyBzYW1wbGVzIGluIGRpZmZlcmVudCBjYXNlIG51bWJlcnMgc28gdGhpcyBtaWdodCBiZSBhbiBhdmVudWUgdG8gZXhwbG9yZSBuZXh0IHRvIHRyeSBhbmQgc2VlIGlmIHRoZXJlIHRydWx5IGlzIGEgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlc2UgZ3JvdXBzLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyA1LjAuMCBDbGFzcyBzdW1tYXJ5DQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovSXN0aGlzX2NsdXN0ZXIucG5nP3Jhdz10cnVlIiB3aWR0aD0iNzAwIi8+DQoNCldoaWxlIHQtU05FIGFuZCBVTUFQIHByb2R1Y2UgcHJvamVjdGlvbnMgdG8gcHJvZHVjZSBjbHVzdGVyZWQgZGF0YSwgeW91IGhhdmUgbm8gcm91dGUgYmFjayB0byB1bmRlcnN0YW5kaW5nIHRoZWlyIHJlbGF0aW9uc2hpcHMuIFBDQSwgb24gdGhlIG90aGVyIGhhbmQsIGlzIHN0cmljdGx5IGEgZGltZW5zaW9uIHJlZHVjdGlvbiB0b29sLiBJdCBkb2VzIG5vdCBwbGFjZSBvciBhc3NpZ24gZGF0YXBvaW50cyB0byBhbnkgZ3JvdXBzIEJVVCBpdCBpcyB1c2VmdWwgdG8gdXNlIG9uIGxhcmdlIGRhdGFzZXRzICpwcmlvciogdG8gY2x1c3RlcmluZyENCjo6Og0KDQpUb2RheSB3ZSB0b29rIGEgZGVlcCBkaXZlIGludG8gcHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcy4gVGhlcmUgYXJlIG9mIGNvdXJzZSBkaWZmZXJlbnQgdmFyaWFudHMgb2YgdGhpcyBiYXNlZCBvbiB0aGUgYXNzdW1wdGlvbnMgeW91IGNhbiBtYWtlIGFib3V0IHlvdXIgb2JzZXJ2YXRpb25zIGFuZCB2YXJpYWJsZXMgbGlrZSBpbmRlcGVuZGVudCBjb21wb25lbnQgYW5hbHlzaXMgKElDQSwgbm9uLUdhdXNzaWFuIGZlYXR1cmVzKSBhbmQgbXVsdGlwbGUgY29ycmVzcG9uZGVuY2UgYW5hbHlzaXMgKE1DQSwgY2F0ZWdvcmljYWwgZmVhdHVyZXMpLiBTb21lIGFkZGl0aW9uYWwgbWV0aG9kcyBjYW4gYWxzbyBiZSB1c2VkIHRvIHN0b3JlIHRoZSB0cmFuc2Zvcm1hdGlvbiBsaWtlIGEgUENBIGRvZXMsIG5vdGFibHkgdmFyaWF0aW9uYWwgYXV0b2VuY29kZXJzIChWQUUpLg0KDQpPdmVyYWxsIHdlIHNob3VsZCByZW1lbWJlciB0aGF0IHdoaWxlIFBDQSBjYW4gaGF2ZSBwcm9ibGVtcyBpbiBnZW5lcmF0aW5nIGl0J3MgZmVhdHVyZSBleHRyYWN0aW9uLCBpdCBpcyAqKipkZXRlcm1pbmlzdGljIGFuZCByZXBlYXRhYmxlKioqLiBBbHNvLCB0aGUgZmluYWwgcmVzdWx0cyBhcmUgcHJvdmlkZWQgaW4gc3VjaCBhIHdheSB0aGF0ICpuZXcqIG9ic2VydmF0aW9ucyBjb3VsZCBiZSB0cmFuc2Zvcm1lZCBhbmQgcHJvamVjdGVkIG9udG8gdGhlIHNhbWUgcHJpbmNpcGFsIGNvbXBvbmVudHMuIFlvdSBjYW4gYWxzbyBmZWVkIHRoZXNlIGNvbXBvbmVudHMgYmFjayBpbnRvIGNsdXN0ZXJpbmcgYWxnb3JpdGhtcyBsaWtlIGstbWVhbnMgdG8gdHJ5IGFuZCBpZGVudGlmeSBzcGVjaWZpYyBzdWJncm91cHMuDQoNCnQtU05FIGFuZCBVTUFQLCBvbiB0aGUgb3RoZXIgaGFuZCBhcHBlYXIgdG8gZG8gYSBtdWNoIGJldHRlciBqb2Igd2l0aCBoaWdoLWRpbWVuc2lvbmFsIGRhdGEuIFRoZXkgY2FuIHByZXNlcnZlIGxvY2FsIHN0cnVjdHVyZSBhbmQgVU1BUCBjYW4gYWxzbyBkbyBhIGZhaXJseSBnb29kIGpvYiBvZiBwcmVzZXJ2aW5nIGdsb2JhbCBzdHJ1Y3R1cmUuIFRoZXNlIHRvb2xzIG1ha2UgZm9yIGdyZWF0IGV4cGxvcmF0b3J5IGFuYWx5c2lzIG9mIHlvdXIgY29tcGxleCBkYXRhc2V0cy4gSW50ZXJwcmV0YXRpb24gb2YgcmVsYXRpb25zaGlwcywgaG93ZXZlciwgYXJlIG5vdCBtYXRoZW1hdGljYWxseSBjbGVhciBsaWtlIGluIFBDQS4gVGhlc2UgYXJlLCBhZnRlciBhbGwgcHJvamVjdGlvbnMgZnJvbSBhIGhpZ2hlciBkaW1lbnNpb24gZm9yIG91ciBzaW1wbGVyIHByaW1hdGUgYnJhaW5zIQ0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNS4xLjAgV2Vla2x5IGFzc2lnbm1lbnQNCg0KVGhpcyB3ZWVrJ3MgYXNzaWdubWVudCB3aWxsIGJlIGZvdW5kIHVuZGVyIHRoZSBjdXJyZW50IGxlY3R1cmUgZm9sZGVyIHVuZGVyIHRoZSAiYXNzaWdubWVudCIgc3ViZm9sZGVyLiBJdCB3aWxsIGluY2x1ZGUgYW4gUiBtYXJrZG93biBub3RlYm9vayB0aGF0IHlvdSB3aWxsIHVzZSB0byBwcm9kdWNlIHRoZSBjb2RlIGFuZCBhbnN3ZXJzIGZvciB0aGlzIHdlZWsncyBhc3NpZ25tZW50LiBQbGVhc2UgcHJvdmlkZSBhbnN3ZXJzIGluIG1hcmtkb3duIG9yIGNvZGUgY2VsbHMgdGhhdCBpbW1lZGlhdGVseSBmb2xsb3cgZWFjaCBxdWVzdGlvbiBzZWN0aW9uLg0KDQp8ICAgICAgICAgICAgICAgICAgICB8IEFzc2lnbm1lbnQgYnJlYWtkb3duIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfDotLS0tLS0tLS0tLS0tLS0tLS06fDotLS0tLS0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgICAgICAgIENvZGUgICAgICAgIHwgICAgICAgICA1MCUgICAgICAgICAgfCBcLSBEb2VzIGl0IGZvbGxvdyBiZXN0IHByYWN0aWNlcz8gICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgIHwgXC0gRG9lcyBpdCBtYWtlIGdvb2QgdXNlIG9mIGF2YWlsYWJsZSBwYWNrYWdlcz8gfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICB8IFwtIFdhcyBkYXRhIHByZXBhcmVkIHByb3Blcmx5ICAgICAgICAgICAgICAgICAgIHwNCnwgQW5zd2VycyBhbmQgT3V0cHV0IHwgICAgICAgICA1MCUgICAgICAgICAgfCBcLSBJcyBvdXRwdXQgYmFzZWQgb24gdGhlIGNvcnJlY3QgZGF0YXNldD8gICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgIHwgXC0gQXJlIGdyb3VwaW5ncyBhcHByb3ByaWF0ZSAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICB8IFwtIEFyZSBjb3JyZWN0IHRpdGxlcy9heGVzL2xlZ2VuZHMgY29ycmVjdD8gICAgIHwNCnwgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgfCBcLSBJcyBpbnRlcnByZXRhdGlvbiBvZiB0aGUgZ3JhcGhzIGNvcnJlY3Q/ICAgICB8DQoNClNpbmNlIGNvZGluZyBzdHlsZXMgYW5kIHNvbHV0aW9ucyBjYW4gZGlmZmVyLCBzdHVkZW50cyBhcmUgZW5jb3VyYWdlZCB0byB1c2UgYmVzdCBwcmFjdGljZXMuIEFzc2lnbm1lbnRzICptYXkqIGJlIHJld2FyZGVkIGZvciB3ZWxsLWNvZGVkIG9yIGVsZWdhbnQgc29sdXRpb25zLg0KDQpZb3UgY2FuIHNhdmUgYW5kIGRvd25sb2FkIHRoZSBtYXJrZG93biBub3RlYm9vayBpbiBpdHMgbmF0aXZlIGZvcm1hdC4gU3VibWl0IHRoaXMgZmlsZSB0byB0aGUgdGhlIGFwcHJvcHJpYXRlIGFzc2lnbm1lbnQgc2VjdGlvbiBieSAxMjo1OSBwbSBvbiB0aGUgZGF0ZSBvZiBvdXIgbmV4dCBjbGFzczogQXByaWwgMTh0aCwgMjAyNC4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDUuMi4wIEFja25vd2xlZGdlbWVudHMNCg0KKipSZXZpc2lvbiAxLjAuMCoqOiBjcmVhdGVkIGFuZCBwcmVwYXJlZCBmb3IgKipDU0IxMDIxSCBTIExFQzAxNDEqKiwgMDMtMjAyMSBieSBDYWx2aW4gTW9rLCBQaC5ELiAqQmlvaW5mb3JtYXRpY2lhbiwgRWR1Y2F0aW9uIGFuZCBPdXRyZWFjaCwgQ0FHRUYuKg0KDQoqKlJldmlzaW9uIDEuMC4xKio6IGVkaXRlZCBhbmQgcHJlcGFyZWQgZm9yICoqQ1NCMTAyMEggUyBMRUMwMTQxKiosIDAzLTIwMjIgYnkgQ2FsdmluIE1vaywgUGguRC4gKkJpb2luZm9ybWF0aWNpYW4sIEVkdWNhdGlvbiBhbmQgT3V0cmVhY2gsIENBR0VGLioNCg0KKipSZXZpc2lvbiAxLjAuMioqOiBlZGl0ZWQgYW5kIHByZXBhcmVkIGZvciAqKkNTQjEwMjBIIFMgTEVDMDE0MSoqLCAwMy0yMDIzIGJ5IENhbHZpbiBNb2ssIFBoLkQuICpCaW9pbmZvcm1hdGljaWFuLCBFZHVjYXRpb24gYW5kIE91dHJlYWNoLCBDQUdFRi4qDQoNCioqUmV2aXNpb24gMi4wLjAqKjogUmV2aXNlZCBhbmQgcHJlcGFyZWQgZm9yICoqQ1NCMTAyMEggUyBMRUMwMTQxKiosIDAzLTIwMjQgYnkgQ2FsdmluIE1vaywgUGguRC4gKkJpb2luZm9ybWF0aWNpYW4sIEVkdWNhdGlvbiBhbmQgT3V0cmVhY2gsIENBR0VGLioNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDUuMy4wIFJlZmVyZW5jZXMNCg0KTW9yZSBpbmZvcm1hdGlvbiBvbiBjYWxjdWxhdGluZyBvcHRpbWFsIGNsdXN0ZXJzOiA8aHR0cHM6Ly93d3cuZGF0YW5vdmlhLmNvbS9lbi9sZXNzb25zL2RldGVybWluaW5nLXRoZS1vcHRpbWFsLW51bWJlci1vZi1jbHVzdGVycy0zLW11c3Qta25vdy1tZXRob2RzLz4NCg0KU3RlcC1ieS1zdGVwIGhvdyBQQ0Egd29ya3M6IDxodHRwczovL2J1aWx0aW4uY29tL2RhdGEtc2NpZW5jZS9zdGVwLXN0ZXAtZXhwbGFuYXRpb24tcHJpbmNpcGFsLWNvbXBvbmVudC1hbmFseXNpcz4NCg0KTW9yZSBQQ0EgZXhwbGFuYXRpb25zIGhlcmU6IDxodHRwczovL3N0YXRzLnN0YWNrZXhjaGFuZ2UuY29tL3F1ZXN0aW9ucy8yNjkxL21ha2luZy1zZW5zZS1vZi1wcmluY2lwYWwtY29tcG9uZW50LWFuYWx5c2lzLWVpZ2VudmVjdG9ycy1laWdlbnZhbHVlcz4NCg0KVGhlIG1hdGggb2YgUENBOiA8aHR0cHM6Ly93d3cuY3MucHJpbmNldG9uLmVkdS9waWNhc3NvL21hdHMvUENBLVR1dG9yaWFsLUludHVpdGlvbl9qcC5wZGY+DQoNCnQtU05FIGluIFIgYW5kIFB5dGhvbjogPGh0dHBzOi8vZGF0YXZpenB5ci5jb20vaG93LXRvLW1ha2UtdHNuZS1wbG90LWluLXIvPg0KDQpBbGwgYWJvdXQgVU1BUDogPGh0dHBzOi8vdW1hcC1sZWFybi5yZWFkdGhlZG9jcy5pby9lbi9sYXRlc3QvYmFzaWNfdXNhZ2UuaHRtbD4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIFRoZSBDZW50ZXIgZm9yIHRoZSBBbmFseXNpcyBvZiBHZW5vbWUgRXZvbHV0aW9uIGFuZCBGdW5jdGlvbiAoQ0FHRUYpDQoNClRoZSBDZW50cmUgZm9yIHRoZSBBbmFseXNpcyBvZiBHZW5vbWUgRXZvbHV0aW9uIGFuZCBGdW5jdGlvbiAoQ0FHRUYpIGF0IHRoZSBVbml2ZXJzaXR5IG9mIFRvcm9udG8gb2ZmZXJzIGNvbXByZWhlbnNpdmUgZXhwZXJpbWVudGFsIGRlc2lnbiwgcmVzZWFyY2gsIGFuZCBhbmFseXNpcyBzZXJ2aWNlcyBpbiBtaWNyb2Jpb21lIGFuZCBtZXRhZ2Vub21pYyBzdHVkaWVzLCBnZW5vbWljcywgcHJvdGVvbWljcywgYW5kIGJpb2luZm9ybWF0aWNzLg0KDQpGcm9tIHRhcmdldGVkIEROQSBhbXBsaWNvbiBzZXF1ZW5jaW5nIHRvIHRyYW5zY3JpcHRvbWVzLCB3aG9sZSBnZW5vbWVzLCBhbmQgbWV0YWdlbm9tZXMsIGZyb20gcHJvdGVpbiBpZGVudGlmaWNhdGlvbiB0byBwb3N0LXRyYW5zbGF0aW9uYWwgbW9kaWZpY2F0aW9uLCBDQUdFRiBoYXMgdGhlIHRvb2xzIGFuZCBrbm93bGVkZ2UgdG8gc3VwcG9ydCB5b3VyIHJlc2VhcmNoLiBPdXIgc3RhdGUtb2YtdGhlLWFydCBmYWNpbGl0eSBhbmQgZXhwZXJpZW5jZWQgcmVzZWFyY2ggc3RhZmYgcHJvdmlkZSBhIGJyb2FkIHJhbmdlIG9mIHNlcnZpY2VzLCBpbmNsdWRpbmcgYm90aCBzdGFuZGFyZCBhbmFseXNlcyBhbmQgdGVjaG5pcXVlcyBkZXZlbG9wZWQgYnkgb3VyIHRlYW0uIEluIHBhcnRpY3VsYXIsIHdlIGhhdmUgc3BlY2lhbCBleHBlcnRpc2UgaW4gbWljcm9iaWFsLCBwbGFudCwgYW5kIGVudmlyb25tZW50YWwgc3lzdGVtcy4NCg0KRm9yIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgdXMgYW5kIHRoZSBzZXJ2aWNlcyB3ZSBvZmZlciwgcGxlYXNlIHZpc2l0IDxodHRwczovL3d3dy5jYWdlZi51dG9yb250by5jYS8+Lg0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L0NBR0VGX25ldy5wbmc/cmF3PXRydWUiIHdpZHRoPSI3MDAiLz4NCjo6Og0K